赞
踩
这场题变多了,但是难度不如上周,G题是道经典的博弈题,H题是一道有趣的图论题,需要优化(过河拆桥)。
这篇主要写E-H的题解,上一篇写A-D的题解。
顺便小小的吐槽下,比赛的机器性能还行,但比赛后的评测机感觉降级了。
Q: 求划分为M段后,最大美味值最小?
最大最小一般就是二分解法,不过这题上界比较大, 是 2e18
本质是对公式
只要能转化为这个式子,那这题基本就稳了
在核心的check逻辑中,从左到右贪心构造满足条件的最大段,然后看段的数量是否<=k
- import java.io.BufferedInputStream;
- import java.util.Scanner;
- import java.util.function.Function;
-
- public class Main {
-
- public static void main(String[] args) {
- Scanner sc = new Scanner(new BufferedInputStream(System.in));
- int n = sc.nextInt();
- int k = sc.nextInt();
-
- long acc = 0;
- long[] arr = new long[n];
- for (int i = 0; i < n; i++) {
- arr[i] = sc.nextLong();
- acc += arr[i];
- }
-
- // 编写check函数和逻辑
- Function<Long, Boolean> check = (m) -> {
- int cnt = 0;
- long acc1 = 0; // 维护\sum ai
- long acc2 = 0; // 维护\sum ai^2
- for (int i = 0; i < n; i++) {
- acc1 += arr[i];
- acc2 += arr[i] * arr[i];
- long x = (acc1 * acc1 - acc2) / 2;
- if (x > m) {
- cnt++;
- acc1 = arr[i];
- acc2 = arr[i] * arr[i];
- }
- }
- cnt++;
- return cnt <= k;
- };
-
- long l = 0, r = 2l * acc * acc;
- while (l <= r) {
- long m = l + (r - l) / 2;
- if (check.apply(m)) {
- r = m - 1;
- } else {
- l = m + 1;
- }
- }
- System.out.println(l);
- }
-
- }

Q: 在一个n*n元组对中,找到第k小的元组,排序规则元组(a, b)中,a+b优先,a其次
有多种解
对于二分的解法,a+b优先,所以采用对角线(y=x+k)作为二分的依据, 这边的长度从1~N,在从N~1,处理起来有点变扭,需要特别注意。
- import java.io.BufferedInputStream;
- import java.util.Scanner;
-
- public class Main {
-
- public static void main(String[] args) {
- Scanner sc = new Scanner(new BufferedInputStream(System.in));
- int t = sc.nextInt();
- while (t-- > 0) {
- int n = sc.nextInt();
- long k = sc.nextLong();
-
- int l = 1, r = 2 * n - 1;
- while (l <= r) {
- int m = l + (r - l) / 2;
- long s = 0;
- if (m <= n) {
- s = (long)(1 + m) * m / 2;
- } else {
- int e = 2 * n - 1 - m;
- s = (long)n * n - (long)(e + 1) * e / 2;
- }
- if (s < k) {
- l = m + 1;
- } else {
- r = m - 1;
- }
- }
- if (l <= n) {
- // 对角线
- int row = (int) (k - (long) l * (l - 1) / 2);
- int col = l + 1 - row;
- System.out.println(row + " " + col);
- } else {
- int e = 2 * n - 1 - l;
- int left = (int)((long)n * n - (long)(e + 1) * e / 2 - k);
- // 存在一个截距
-
- int s = l + 1 - n;
- int col = s + left;
- int row = l + 1 - col;
- System.out.println(row + " " + col);
- }
- }
- }
-
- }

Q: 在一个数组中,必须按照斐波那契步长跳跃(比前一次更长,且目标位置数大于当前值),谁不能行动,谁就认输?
很经典的博弈题,寻找必胜态和必败态
经典的alpha+beta剪枝解法
数组很长,但是斐波那契数组增长非常快,10^5范围内,只有24项。
如果令 dp[i][s], i为位子,s为步长,实际只有O(24*n)的状态总量
借助记忆化搜索,可以快速求解出来。
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.util.*;
- import java.util.stream.Collectors;
-
- public class Main {
-
- static class Solution {
- List<Integer> steps;
- int n;
- int[] arr;
-
- Map<Long, Boolean> memo = new HashMap<>();
-
- public void ready(int n, int[] arr, List<Integer> steps) {
- this.n = n;
- this.arr = arr;
- this.steps = steps;
- }
-
- public boolean dfs(int u, int s) {
- long key = (long)u << 32 | s;
- if (memo.containsKey(key)) {
- return memo.get(key);
- }
- for (int i = s; i < steps.size(); i++) {
- int jump = steps.get(i);
- if (u + jump < n && arr[u] < arr[u+ jump]) {
- if (!dfs(u + jump, i + 1)) {
- memo.put(key, true);
- return true;
- }
- }
- if (u - jump >= 0 && arr[u] < arr[u - jump]) {
- if (!dfs(u - jump, i + 1)) {
- memo.put(key, true);
- return true;
- }
- }
- }
- memo.put(key, false);
- return false;
- }
- }
-
- public static void main(String[] args) {
- AReader sc = new AReader();
- int n = sc.nextInt();
- int[] arr = new int[n];
- for (int i = 0; i < n; i++) {
- arr[i] = sc.nextInt();
- }
-
- // 预处理
- List<Integer> steps = new ArrayList<>();
- int f0 = 1, f1 = 1;
- while (f1 < n) {
- steps.add(f1);
- int t = f1 + f0;
- f0 = f1;
- f1 = t;
- }
-
- List<String> result = new ArrayList<>();
- Solution solution = new Solution();
- solution.ready(n, arr, steps);
- for (int i = 0; i < n; i++) {
- boolean r = solution.dfs(i, 0);
- result.add(r ? "Little Lan" : "Little Qiao");
- }
- System.out.println(result.stream().collect(Collectors.joining("\n")));
- }
-
- static
- class AReader {
- private BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
- private StringTokenizer tokenizer = new StringTokenizer("");
- private String innerNextLine() {
- try {
- return reader.readLine();
- } catch (IOException ex) {
- return null;
- }
- }
- public boolean hasNext() {
- while (!tokenizer.hasMoreTokens()) {
- String nextLine = innerNextLine();
- if (nextLine == null) {
- return false;
- }
- tokenizer = new StringTokenizer(nextLine);
- }
- return true;
- }
- public String nextLine() {
- tokenizer = new StringTokenizer("");
- return innerNextLine();
- }
- public String next() {
- hasNext();
- return tokenizer.nextToken();
- }
- public int nextInt() {
- return Integer.parseInt(next());
- }
-
- public long nextLong() {
- return Long.parseLong(next());
- }
-
- // public BigInteger nextBigInt() {
- // return new BigInteger(next());
- // }
- // 若需要nextDouble等方法,请自行调用Double.parseDouble包装
- }
-
- }

Q: 给了一个序列数组,但是按照规则可以构建边(双向边)形成一个稠密图,求A点到B点的最小距离。
这题难点在于
稠密图
拿最简单的BFS求路径来说,其时间复杂度为, V是顶点数,E是点V的均摊边数,这题容易TLE,本质的原因就在这里。假设E=V, 那就是
那如何求解这题呢?
这边有个概念,叫做组(同一类)
可以按组去存放这些点集列表,因为BFS的特性
里面的点集必然,最多相差1
如果从一个点出发,已经通过这个组(点集)遍历了其他节点。那么其他点(同属组),在遍历这个组,就没必要了,因为没有增益优化的空间了。可以细细评味这个。
所以把这个技巧,称之为
过河拆桥
至于质因子拆解,这边就不展开讲了。
这题题意还是有点绕,比赛的时候,看了很久,都懵圈了。
- import java.io.BufferedInputStream;
- import java.util.*;
-
- public class Main {
-
- static List<Integer> primes = new ArrayList<>();
-
- static {
- int mz = 10000;
- boolean[] vis = new boolean[mz + 1];
- for (int i = 2; i <= mz; i++) {
- if (!vis[i]) {
- primes.add(i);
- for (int j = i * i; j <= mz; j+=i) {
- vis[j] = true;
- }
- }
- }
- }
-
- static int solve(int v) {
- int res = 0;
- for (int i = 0; i < primes.size(); i++) {
- int t = primes.get(i);
- if (v < t) break;
- if (v % t == 0) {
- while (v % t == 0) {
- res += t;
- v /= t;
- }
- }
- }
- if (v > 1) {
- res += v;
- }
- return res;
- }
-
- public static void main(String[] args) {
-
- Scanner sc = new Scanner(new BufferedInputStream(System.in));
-
- int n = sc.nextInt();
- int a = sc.nextInt(), b = sc.nextInt();
-
- List<Integer> []g = new List[n + 1];
- Arrays.setAll(g, x -> new ArrayList<>());
-
- int[] values = new int[n + 1];
- int[] arr = new int[n + 1];
- Map<Integer, List<Integer>> hash = new HashMap<>();
- for (int i = 1; i <= n; i++) {
- arr[i] = sc.nextInt();
- values[i] = solve(arr[i]) % n + 1;
- hash.computeIfAbsent(values[i], x -> new ArrayList<>()).add(i);
-
- if (values[i] >= 1 && values[i] <= n && values[i] != i) {
- g[i].add(values[i]);
- g[values[i]].add(i);
- }
- }
-
- int inf = Integer.MAX_VALUE / 10;
- int[] res = new int[n + 1];
- Arrays.fill(res, inf);
-
- Deque<Integer> deq = new ArrayDeque<>();
- deq.offer(a);
- res[a] = 0;
-
- while (!deq.isEmpty()) {
- int u = deq.poll();
- int w = values[u];
-
- List<Integer> edges = hash.getOrDefault(w, Collections.EMPTY_LIST);
- for (int v : edges) {
- if (v == u) continue;
- if (res[v] > res[u] + 1) {
- res[v] = res[u] + 1;
- deq.offer(v);
- }
- }
- hash.remove(w);
-
- for (int v: g[u]) {
- if (v == u) continue;
- if (res[v] > res[u] + 1) {
- res[v] = res[u] + 1;
- deq.offer(v);
- }
- }
- }
- System.out.println(res[b] == inf ? -1 : res[b]);
-
- }
-
- }

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。