所有效率之和等于零。
1 2 3 4 5 6 7
| public static void solve() { int n = io.nextInt(), sum = 0; for (int i = 0; i < n - 1; i++) { sum += io.nextInt(); } io.println(-sum); }
|
贪心选择最小花费的通知,注意维护已经被通知的下标,根据该下标判断是否需要加 \(p\),并且如果某个人通知其他人的成本大于 \(p\),则他不会通知其他人。发现大佬的解法好简单,取一个最小值,维护一个剩余人数即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public static void solve() { int n = io.nextInt(), p = io.nextInt(); int[] a = new int[n]; for (int i = 0; i < n; i++) { a[i] = io.nextInt(); } int[] b = new int[n]; for (int i = 0; i < n; i++) { b[i] = io.nextInt(); }
var aux = new Integer[n]; for (int i = 0; i < n; i++) { aux[i] = i; } Arrays.sort(aux, (i, j) -> b[i] - b[j]);
long ans = p; int r = n - 1; for (int i : aux) { int x = Math.min(r, a[i]); ans += (long) x * Math.min(p, b[i]); r -= x; } io.println(ans); }
|
分类讨论,注意被 \(n\) 整除的数。
1 2 3 4 5 6 7
| public static void solve() { int n = io.nextInt(), m = io.nextInt(), k = io.nextInt(); if (k == 3) io.println(Math.max(0, m - n - m / n + 1)); else if (k == 2) io.println(m / n + Math.min(n - 1, m)); else if (k == 1) io.println(1); else io.println(0); }
|
很容易的想到计算选择每个索引位置,能够得到的最大分数,时间复杂度为 \(O(n\log{n})\)。然后计算每个位置有多少种方案,比赛时我以为选择一个索引位置,默认就选择了它的所有倍数位置,导致不会做。其实只要排序,然后每个位置的方案数就是它左边的数选或不选的方案数,这样可以保证不会重复计算,所以快速幂计算方案数(可以在遍历的时候计算方案数),再乘以得分累加到答案即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| private static final int MOD = 998244353;
public static void solve() { int n = io.nextInt(); int[] dp = new int[n + 1]; for (int i = 1; i <= n; i++) { dp[i] = io.nextInt(); }
dp[0] = Integer.MAX_VALUE; for (int i = 1; i <= n; i++) { for (int j = 2 * i; j <= n; j += i) { dp[i] = Math.max(dp[i], dp[j]); } }
Arrays.sort(dp); long ans = 0L, pow = 1L; for (int i = 0; i < n; i++) { ans = (ans + dp[i] * pow) % MOD; pow = pow * 2 % MOD; } io.println(ans); }
|
方法一:内向基环树
对于每个 \(i\),建立一条从 \(i\) 指向 \(a_{i}\) 的边,最终会得到多个内向基环树。规则一:入度为零的节点不会被选择;规则二:如果一个节点的所有入度节点都被选择,那么它不会被选择;规则三:如果一个节点不被选择,那么它指向的节点会被选择。(以上规则可以使用拓扑序 + 染色法实现)
如图,数组为 \([2,3,4,5,6,3,3,4]\),剩余索引 \([1,5,7,8]\),剩余元素 \([2,6,3,4]\),选择索引 \([2,6,3,4]\)。应用规则一,得出索引 \([1,7,8]\) 不被选择;应用规则三,得出索引 \([2,3,4]\) 被选择;应用规则二,得出索引 \([5]\) 不被选择;应用规则三,得出索引 \([6]\) 被选择。
特殊情况,如果内向基环树没有环外节点,无法应用上述规则,那么需要单独对环交替染色,若此时环的长度为奇数,则没有解,其他情况均有解。也就是说,当且仅当内向基环树没有环外节点,且环的长度为奇数时,无解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| public static void solve() { int n = io.nextInt(); int[] a = new int[n]; for (int i = 0; i < n; i++) { a[i] = io.nextInt() - 1; }
int[] in = new int[n]; for (int i = 0; i < n; i++) { in[a[i]]++; }
int[] col = new int[n]; Queue<Integer> q = new LinkedList<>(); for (int i = 0; i < n; i++) { if (in[i] == 0) { q.offer(i); col[i] = -1; } } while (!q.isEmpty()) { int x = q.poll(), y = a[x]; if (col[y] != 0) continue; if (--in[y] == 0 || col[x] == -1) { q.offer(y); col[y] = -col[x]; } }
for (int i = 0; i < n; i++) { if (col[i] != 0) continue; int j = i, pre = -1; while (col[j] == 0) { col[j] = -pre; pre = col[j]; j = a[j]; } if (col[j] == pre) { io.println(-1); return; } }
List<Integer> ans = new ArrayList<>(); for (int i = 0; i < n; i++) { if (col[i] == -1) ans.add(a[i] + 1); } io.println(ans.size()); for (int x : ans) { io.print(x + " "); } io.println(); }
|
方法二:外向基环树
好像有建立外向基环树的解法,没时间看,在此。