[做题记录-乱做] 20211223~20211224
All Kill
All Kill - Kattis allkill - Virtual Judge (vjudge.net)
一场 ICPC 比赛共有 \(n\) 个题, 比赛时长为 \(t\) 分钟. 第 \(i\) 个题在想出来之后需要花费 \(a_i\) 的时间来写. 你会在 \(b_i\) $∈ {0, 1, 2, · · · ,t − 1} $这个时刻想出第 \(i\) 个题的做法. 你写题的策略是选择当前会做的编号最小的题目写. 如果在写一个题目 的过程中想出了一个编号更小的题目的做法, 你就会不高兴. 求有多少种序列 \(b_i\) 让你 AK 且高兴.
\(n \leq 10^5\)
这个题有两个目标, 一个是高兴, 一个是 AK 。
这个 AK 其实很难搞, 考虑在 \(t + 1\) 时刻加入一个空位, 强制这个时刻不做事, 然后首尾相连。
设 一个 \(t' = t - \sum a_i\) 。
那么这个时候事件的序列其实被分成了 \(t' + n + 1\) 段, 考虑在这个环上面放事件, 那就是第一个事件有 \(t' +n + 1\) 个放法, 第二个有 \(t'+ n + a_1\) 个放法, 以此类推。
然后由于那个人为添加的位置放到任意一个地方都是一样的, 最后除以一个 \(t' + n + 1\) 就是答案了。
[AGC020F] Arcs on a Circle
高端。
可以发现整数部分判是不是连接上只和位置的整数部分,和小数部分相对大小相关, 因为两个小数相等的概率是 \(0\), 所以可以枚举小数部分相对大小做状压。
时间复杂度 $ O(n! n2cc2^n)$ 。
还有一个通过不断细分得到的贵族做法 题解 。
Clique
给一个圆的 n 条圆弧, 选出一个尽量大的子集使得其中的圆弧两两有交.
\(n \leq 3000\)
枚举一条必须选的圆弧, 分类讨论其他圆弧。
最后剩下和左边相交的和右边相交的, 判断左右如何相交, 转化为数据结构问题, 直接线段树优化即可。
Graph Coloring
给一个 n 个点的竞赛图, 给边 14 染色, 使得对于任意 i → j → k, i → j 和 j → k 不同色.
\(n \leq 3000\)
由于 $\binom{14}{7} >3000 $, 可以给每个点一个独一无二的有 7 个 0 和 7 个 1 的 01 串. 对于每条边, 一定有某一位从 0 变成了 1, 将边染色成这个位对应的颜色 即可.
[AGC013D] Piling Up
[Link]([AT2370 AGC013D] Piling Up - 洛谷 | 计算机科学教育新生态 (luogu.com.cn))
老白给了。
把操作序列中黑球个数的变化记录下来就是一个折线, 数折线即可。
但是直接 dp 折线会重复, 所以只考虑最下面那根折线就好了, 也就是考虑 “脸着地” 过的折线。
具体来说设 \(f_{i, j, k}\) 表示当前第 \(i\) 次操作, 有 \(j\) 个黑球, 有没有脸着地过即可。
Luogu P6944 [ICPC2018 WF]Gem Island
有意思的题。
[lg6944][loj6406][CodeforcesGym102482D][WF2018]Gem Island (yhx-12243.github.io)
考虑 \(d\) 次操作以后的序列 \(A\), 根据人类情感, 可以知道每个序列出现的概率是一样的。考虑计算所有的序列的前 \(k\) 大和然后除一个组合数。
这个序列的和是不大的, 意味着我们可以像划分数一样维护这个序列。
考虑枚举序列中不为 \(0\) 的位置个数, 然后设 \(f_{i, j}\) 表示当前有 \(i\) 个非 \(0\) 位置, 和为 \(j\) 的和。
转移就像划分数一样枚举当前这层有多少数, 累加贡献转移即可, 注意只要前 \(r\) 大的和, 但是因为我们本来就是维护了一个有序的形状, 可以简单做到这些。
BZOJ 2688 绿色博弈
一棵树的 SG 值等于所有以 "1 的子节点" 为根的子树的 SG 值加 1 后的异或和。
UOJ 279 【UTR #2】题目交流通道
【UTR #2】题目交流通道 - 题目 - Universal Online Judge (uoj.ac)
好题。
首先考虑判无解, 直接判断自反性, 三角形不等式, 以及边权有没有超即可。
然后考虑计数。
首先考虑边权全部大于 \(0\) 的情况, 那么这个时候对于一对点 \((i, j)\) , 如果存在点 \(k\) 使得 \(d(i, j) = d(i, k) +d(k, j)\) 那么 \(w_{i, j}\) 只要大于等于 \(d(i, j)\) 就好了。
现在考虑边权有 \(0\) 的情况。
首先说说为啥要讨论有没有 \(0\) 边,没有的时候可以确定某两个点之间的距离是有谁贡献来的, 但是有的时候, 当前的这个 \(0\) 边不知道会贡献给谁也不知道被谁贡献, 换句话来说就是 \(0\) 边连接的联通块没有一个确切的贡献顺序, 之间按照上面的方法算会出问题。
考虑 \(0\) 边形成的联通块, 我们要求他们可以被 \(0\) 边连起来即可。 那么不妨把最后是 \(0\) 权边的边视为存在, 不是 \(0\) 权边的边视为不存在, 那么就是要统“联通图”的贡献。
这是一个经典容斥, 设\(F(n)\) 表示 \(n\) 个点联通图的贡献, \(G(n)\) 表示“图” 的贡献。有:
大力干就好了。
CF1383E Strange Operation
比较白给。
拿出来最左边的和最右边的 \(1\), 可以观察到左右的 \(0\) 独立, 不管, 最后乘 \((L +1) \times (R+1)\) 即可。
中间的部分注意到中间被划分成了一段一段的部分, 直接搞即可, 钦定 \(1\) 被删掉的时候在左边。
UOJ 311 【UNR #2】积劳成疾
由于序列的每个位置是独立的, 考虑一个关于序列位置的 \(dp\) 。显然, 我们除了长度, 还需要一个当前的最大值来刻画这个序列。
尝试设 \(f(i, j)\) 表示序列长度为 \(i\) 最大值 \(\leq j\) 的所有序列的权值和。
那么 \(j <K\) 的时候答案就是 \(i^j\), 因为所有序列答案都是 \(j\) 。
然后考虑其他的转移。
首先考虑最大值不是 \(j\) 的情况, 直接从 \(f(i, j - 1)\) 转移过来即可。
然后枚举第一个最大值的位置, 把序列分成左右两段。因为位置是独立的, 可以直接递归下去。
转移类似如下:
for(int i = 1; i <= n; i ++) {
read(pw[1]);
for(int j = 2; j <= n; j ++) pw[j] = 1ll * pw[j - 1] * pw[1] % P;
f[i][0] = 1;
for(int j = 1; j < K; j ++) f[i][j] = 1ll * f[i][j - 1] * i % P;
for(int j = K; j <= n; j ++) {
f[i][j] = f[i - 1][j];
for(int k = 1; k <= j; k ++)
f[i][j] = (f[i][j] + 1ll * f[i - 1][k - 1] * f[i][j - k] % P * pw[min(j + 1, k + K) - max(k, K)]) % P;
}
}