2022 暑假做题记录
6.23
「ARC076C」Connected?(思维)
发现只有边界上的会有影响,那么把它们拿出来,不合法就是存在一对点在这一对点的连线的两边,这个可以类似括号序列那样记一个前缀和就行。
Code:https://atcoder.jp/contests/arc076/submissions/32671721。
「ARC067D」Yakiniku Restaurants(单调栈 + 二维差分)
对每张券分开考虑。
考虑一张券 \(i\) 在第 \(j\) 个商店用的情况,我们可以用单调栈求出左边第一个价值 \(>b_{j,i}\) 的位置 \(l\) 和右边第一个价值 \(\ge b_{j,i}\) 的位置 \(r\),那么左端点在 \([l,j]\)、右端点在 \([j,r]\) 的区间会用这张券。
然后可以做一个二维差分,最后枚举线段左端点和右端点即可。
Code:https://atcoder.jp/contests/arc067/submissions/32673041。
2022.6.23 考试 T2 蒸蒸日上(upupup)(DAG 的支配树 + 虚树)
题面:
【题目描述】
小 M 非常不擅长数学。
小 M 在学习数学里的「集合」部分时就被一道题难住了。
题目会给出 \(n\) 个集合 \(A_{1\dots n}\),满足 \(A_i\) 里的元素都严格小于 \(i\)。
可以通过这些集合构造另外 \(n\) 个集合 \(B_{1\dots n}\),其中 \(B_i = \{i\} \cup (\cap _{k\in A_i} B_k )\),也就是所有以 \(A_i\) 中元素为下标的 \(B\) 集合的公共部分再额外加入一个 \(i\)。
小 M 读完题,写下所有的 \(B\) 集合之后就不会做了。
但小 M 很好奇这些集合的性质,所以他会询问你 \(q\) 次若干个 \(B\) 集合的并集的大小,通过了这题他的数学水平就能蒸蒸日上了。
【输入格式】
从文件
upupup.in中读入数据。第一行一个整数 \(n\) 表示集合个数。
接下来 \(n\) 行,每行第一个数 \(c_i\) 表示第 \(i\) 个集合的数的个数,接下来是 \(c_i\) 个小于 \(i\) 的数,代表 \(A_i\) 中的元素。
接下来一行一个整数 \(q\) 表示询问组数。
接下来 \(q\) 行,每行第一个数 \(k_i\) 表示当前询问涉及到的集合数量,接下来 \(k_i\) 个整数分别表示询问的集合 \(B\) 的编号。
【输出格式】
输出到文件
upupup.out中。共 \(q\) 行,每行一个整数表示对应若干 \(B\) 集合的并的大小。
我们将 \(A_i\) 中的所有元素向 \(i\) 连一条边,那么这张图就是一个 DAG。特别的,建一个虚点 \(n+1\) 向所有入度为 \(0\) 的点连边。
可以发现如果一个数 \(x\) 在集合 \(B_i\) 中,那么所有从 \(n+1\) 到 \(i\) 的路径都要经过 \(x\),即 \(x\) 支配 \(i\)。
建出原 DAG 的支配树,那么问题就变成选若干个点它们到根的路径的并的长度,这个可以通过虚树求出。
此题中可以不用显式建出虚树,直接求出 \(\sum dep\) 后按照 \(dfn\) 排序,减去相邻两个点的 LCA 的 \(dep\)。
Code:https://pastebin.ubuntu.com/p/7N3m9hWHtk/。
6.24
2022.6.24 考试 T1 七管荧光灯(qgygd)(找规律 + 博弈论 + 数位 dp)

我们打表可以发现,先手必败的状态是 \(1, 2, 3\) 号管上的数相等,\(5, 6, 7\) 号管上的数相等,并且 \(1\) 号管上的数,\(4\) 号管上的数和 \(5\) 号管上的数异或和为 \(0\)。
证明十分容易,我们先考虑游戏的终止状态,也就是所有的管子上的数都是 \(0\),显然异或和为 \(0\),必败态显然无法转移至必败态。
接下来考虑证明必胜态一定能转移到必败态,我们取出 \(1, 2, 3\) 号管中最小值所在的管子,记为 \(x\),\(5, 6, 7\) 号管中最小值所在的管子,记为 \(y\),先将 \(x, y,4\) 这些管子放在一边,剩下的管子全选,然后再选 \(x, y, 4\) 中值最大的,容易证明不会成环,显然可以通过调整这些管子使其到达必败态。
在知道这个结论之后,我们可以用数位 dp 求出必败态的种类数,简单来说,就是先容斥为求只有最大值限制的方案数,然后再设 \(f_{i,0/1,0/1,0/1}\) 表示当前 dp 到了第 \(i\) 位,第 \(1, 2, 3\) 号管是否达到上界,\(4\) 号管是否达到上界,\(5, 6, 7\) 号管是否达到上界,时间复杂度 \(O(\log n)\)。
Code:https://pastebin.ubuntu.com/p/BDgh2hSRMw/。
「APIO2016」划艇(计数 dp)
发现值域有点大,于是把所有的点拿出来离散化一下,假设这个序列为 \(\{c\}\)(可以改成左闭右开的区间方便计数)。
设 \(f_{i,j}\) 表示考虑到第 \(i\) 所学校,它派出的划艇数量在 \([c_j,c_{j+1})\) 中的方案数。
显然若 \([c_j,c_{j+1})\) 不被 \([a_i,b_i+1)\) 包含,那么 \(f_{i,j}=0\)。
引理:在 \([1,k]\) 中至多选出 \(m\) 个不相等的数的方案数是 \(\binom{k+m}{m}\)。
证明:可以看成从 \(k\) 个球中选出至多 \(m\) 个球,那么在前面再加上 \(m\) 个球,问题就变成从 \(k+m\) 个球里选 \(m\) 个球,直接组合数即可。
我们枚举上一个派出的划艇数量不在 \([c_j,c_{j+1})\) 中的学校 \(k\),它派出的划艇数量在 \([c_l,c_{l+1})\) 中。假设 \(k+1\sim i\) 中有 \(cnt\) 个学校可以派出在 \([c_j,c_{j+1})\) 中的划艇数量,那么转移系数就是 \(\binom{c_{j+1}-c_j+cnt-1}{cnt}\),-1 的原因是不能一个都不选。即 \(f_{i,j}=\sum\limits_{k=0}^{i-1}\sum\limits_{l=0}^{j-1}\binom{c_{j+1}-c_j+cnt-1}{cnt}f_{k,l}\)。
不难发现转移系数与 \(l\) 无关,提到前面前缀和优化即可。
Code:https://loj.ac/s/1490826。
「洛谷 P7091」数上的树(dp)
设 \(dp_x\) 为以 \(x\) 为根的最小答案。
转移可以直接枚举 \(n\) 的因数。
Code:https://pastebin.ubuntu.com/p/fXB2Zh3Rqr/。
「CF1156F」Card Bag(概率 dp + 差分优化)
容易发现取出卡片数字是递增的。
设 \(f_{i,j}\) 为取了 \(i\) 张卡片,最后一张取出的数字是 \(j\) 的概率。
容易有转移:\(f_{i,j}\times\frac{cnt_k}{n-i}\to f_{i+1,k}(k>j)\)。
发现它贡献的是一段后缀,可以差分去做。
Code:https://pastebin.ubuntu.com/p/jDMBNSvCZg/。
6.25
2022.6.25 考试 T1 无题(noname)(最小生成树 + 计算几何)

度数为 2 直接求,为 4 枚举三种情况,取较小值。这是假的。
由于图存在欧拉回路,因此所有点的度数都必然是偶数。因而只存在度数为 \(2\) 的和度数为 \(4\) 的点。
对于度数为 \(2\) 的点 \(u\),显然在 \(u\) 处转过的角度是固定的。直接计算夹角累加到答案中即可。
对于度数为 \(4\) 的点 \(a\),不妨设 \(4\) 条边分别为 \((a, b_{1\sim4})\),那么只有三种分组方式:即 \((a, b_1 )\) 和 \((a, b_{2\sim4} )\) 中的某个形成一组,剩下的两个形成另一组。这里边 \((x, y)\) 与边 \((x, z)\) 分在一组中的意义是如果走 \(y \to x\),那么下一步必须走 \(x \to z\);而如果走 \(z \to x\),那么下一步必须走 \(x \to y\)。贪心地,我们选择三种方案中转过的角度和最小的那种即可。
然而这样做可能会形成若干条欧拉回路,并不满足条件。考虑我们可以这样调整:对于一个 \(4\) 度点 \(u\),假如 \((u, v_1 ), (u, v_2 )\) 在一组,\((u, v_3 ), (u, v_4 )\) 在另一组,且两组分别在两条欧拉回路中,那么我们就可以通过将分组方式调整为另外两种中转过角度和较小的一种从而将两条欧拉回路合并。不妨设调整代价为 \(dt_u\)。
我们将每条欧拉回路看作点,回路间的交点(即满足上面点 \(u\) 性质的点)看作边,边权为对应的 \(dt\)。现在要求是选择一些边使得图连通。不难发现这是一个最小生成树问题,于是直接按边权排序同时用并查集维护即可。
时间复杂度为 \(O(n\log n)\)。
求角度可以用余弦定理或者求出两边的角度后作差。
Code:https://pastebin.ubuntu.com/p/V6ystPxNy5/。
2022.6.25 考试 T2 人气投票(vote)(思维 + 位运算 + 贪心)
题意:
给出 \(n\) 个数 \(a_i\),每次操作可以给一个数 \(+1\),问最少多少次操作后所有数异或和为 \(0\)。
多组数据,数据组数 \(T\le10^3\),\(\sum n\le 10^6\),\(0\le a_i<2^{60}\)。
思维好题。
引理 1:若初始序列中 \(a_i<a_j\),那么最终序列也有 \(a_i<a_j\)。
证明:若不满足,则交换最终两个数更优。
设 \(X\) 为整个序列的异或和,只用考虑 \(X \not = 0\) 的情况。
考虑 \(X\) 最高的为 \(1\) 的位,假设是第 \(b\)
接下来的讨论在「存在至少一个第 \(b\) 位为 \(0\) 的数」的假设下进行。
我们必须在这一位上做一些手脚来让这一位变成零,无非是两种情况:
- 把这一位为 \(0\) 的数变成 \(1\);
- 把这一位为 \(1\) 的数变成 \(0\)。
引理 2:不会进行第二类操作。
证明:
首先根据假设,答案不超过 \(X\)。
那么首先一定不存在 \(0 \to 1 \to 0 \to 1\) 或者 \(1 \to 0 \to 1 \to 0\) 的情况,否则其代价已经超过了 \(X\)。
其次,不存在 \(0 \to 1 \to 0\) 和 \(1 \to 0 \to 1\) 的情况,因为它在做无用功。
于是一定只是单纯的 \(0 \to 1\) 和 \(1 \to 0\)。
最后只需注意到第二种操作每次都会让前一位加一,也即让前一位的异或和异或上 \(1\),那么这种操作必须只能做偶数次,进而可以推出不如不做这些操作。
所以可以知道不会进位,那么序列的答案和序列中每个数只保留最低 \(b\) 位的值的答案相同。
于是我们现在要做的无非是找奇数个这一位为 \(0\) 的,然后把它变成 \(1000\dots\) 的形式,然后递归到子问题。
可以只考虑最后 \(b\) 位,第 \(b\) 位为 \(0\) 的最大的数一定会变成 \(1\),那么找出这个数,暴力操作然后递归到子问题即可。
注意到操作之后这个数后面的位都是 \(0\),于是依然满足假设,那么这个递归是合法的。
如果 \(a\) 中存在为 \(0\) 的数,则如下的算法是正确的:
- 找到异或和的最高的非零位;
- 找到只保留 \(\le\) 这一位的位的情况下,这一位为 \(0\) 的最大的数 \(p\);
- 把 \(p\) 暴力变成 \(1000\dots\) 的形式;
- 递归到子问题。
考虑现在异或和的最高位可能没有为 \(0\) 的数,于是考虑人为创造一个。
考虑由于这一位全部是 \(1\),于是可以推知一定对前面的位有进位。那么考虑最后 最高的 和初始情况不完全相同的位,那么这一位一定存在至少两个 \(0\) 变成了 \(1\),那么根据引理 1,这一定是最大的两个数。
那么只需要枚举最后最高的不同位,然后暴力把最大的两个为 \(0\) 的变成 \(1000\dots\) 的形式,然后我们就在之后的位上得到了这一位为 \(0\) 的数,也即可以调用刚才的做法处理。
复杂度 \(O(n\log^2V)\)。
Code:https://pastebin.ubuntu.com/p/bWX3BZ3Nr2/。
6.27
2022.6.27 考试 T1 基础概率练习题(probability)(概率)
题意:
对于一个长度为 \(n\) 的非负整数序列 \(a_1 , a_2 ,\dots, a_n\),已知 \(a_1 \ge k\),\(\sum a_i=m\),询问 \(a_1\) 是最大值的概率,如果序列中有多个最大值,随机任意一个作为最大值。答案对 \(998244353\) 取模。
\(1\le n,m,k\le 10^7\),\(k\le m\)。
加强自 CF1096E。
考虑条件概率 \(P (A | B) =\frac{P(AB)}{P(B)}\) 表示在 B 发生的情况下 A 发生的概率,就等于 AB 同时发生的概率除以 B 发生的概率。这题中 A 是第一个人 rk1,B 是第一个人得分 \(\ge k\)。
那么 \(p_1 = P (B)\) 就是拿第一个人得分 \(\ge k\) 的方案数去除以没有任何限制的方案数。\(p_2 = P (AB)\) 就是第一个人 rk1 且得分 \(\ge k\) 的概率。我们发现对于每个人,这个概率是相同的,并且只要有人得分 \(\ge k\) 那么就一定会对这个概率产生贡献(rk1 得分一定 \(\ge k\))。
那么 \(n \times p_2\) 就是拿至少一个人的得分 \(\ge k\) 的情况除以没有任何限制的情况,然后就能直接解出 \(p_2\)。
\(\frac{p_2}{p_1}\) 就是答案。时间复杂度 \(O(n + m)\)。
Code:https://pastebin.ubuntu.com/p/shg7V5CKwn/。
2022.6.27 考试 T2 基础树剖练习题(chain)(树剖 + 重标号 + 线段树)

数据范围:\(1\le n,q\le 10^5\),\(fa_i<i\)。
这种题一般可以考虑树剖后对树重标号,然后用线段树多维护几个信息解决。
把边的信息转移到其下端的点上。
首先还是树剖,对于一条重链,我们从上到下依次标号,然后对于从上到下每个点的轻儿子按顺序标号,记录下每个点轻儿子的标号区间和其所有轻儿子子树的标号区间。
线段树上先预处理出区间内深度为奇数 / 偶数的点数,维护的信息大概是区间内深度为奇数 / 偶数的点是黑边的条数。
修改时直接跳链,注意还有当前点的重儿子与上一个链顶要讨论。查询链可以直接查询,子树利用之前记录的两个标号区间也可以很方便地处理。
Code:https://pastebin.ubuntu.com/p/48j6WSfX3b/。
6.28
「CF1542E2」Abnormal Permutation Pairs (hard version)(dp)
懒得写题解了,贴个链接:https://www.cnblogs.com/alex-wei/p/CF1542E2.html。
Code:https://pastebin.ubuntu.com/p/qqBDFMgZmB/。
「JOISC 2017 Day 3」幽深府邸 & 「HNOI/AHOI2018」游戏 & 2022.6.28 考试 T1 局部坏死(haohuo)(暴力 dfs)
先算出离打开每扇门所需的钥匙左右最近的位置,然后对于每个没有标记过的点暴力向左右扩展,更新能到的左右范围,最后判断一下就行。复杂度均摊大概是 \(O(n)\)。
Code:https://loj.ac/s/1493872。
「HNOI2011」卡农 & 2022.6.28 考试 T3 颜值平均眼镜(glasses)40pts(计数 dp)
题意:
从 \(1,2,\dots,2^n-1\) 中无序选出 \(m\) 个不同的数,使得它们异或和为 \(0\),问方案数对 \(10^8+7\) 取模的结果。
\(1\le n,m\le10^6\)。
无序不太好算,先转成有序再在最后除以 \(m!\)。
设 \(f_i\) 表示选择 \(i\) 个数异或和为 \(0\) 的方案数。
先考虑前 \(i-1\) 个随便选,最后一个为前 \(i-1\) 个的异或和的情况(方案数为 \(A_{2^n-1}^{i-1}\)),再减去不合法的情况,有两种:
- 前 \(i-1\) 个数异或和为 \(0\),方案数为 \(f_{i-1}\);
- 第 \(i\) 个数和前 \(i-1\) 个数中的一个相等,可以考虑是与哪个相等,这个数是多少,那么剩下 \(i-2\) 个数异或和为 \(0\),方案数为 \((i-1)\times(2^n-1-(i-2))\times f_{i-2}\)。
递推即可。
Code:https://pastebin.ubuntu.com/p/RT2n7sQ4sm/。
6.29
2022.6.29 考试 T1 摸黑深巢(deepnest)(同余最短路)
题意:
给定一个数组。对于每个前缀 \(i\),求 \(f (a_1 , a_2 , a_3 , \dots, a_i )\)。
其中,\(f (\{a_n \})\) 定义为最大的不在集合 \(\{\sum\limits_{j=1}^n b_j a_j \}\) 中出现的数,其中 \(b\) 中的每个元素为非负整数。
如果每个自然数均在集合中出现,定义 \(f (S) = −1\)。
如果答案无穷大,输出
INF。\(1\le n\le 100,1\le a_i\le 40000\)。
考虑同余最短路。对于每个 \(u\),向 $u+a_i\ \rm mod\ a_1 $ 连权值为 \(a_i\) 的边。那么 \(\max\{dis_i-a_1\}\) 就是答案,其中 \(a_1\) 为最小值。
直接 spfa/Dijkstra 就可以过。一方面原题是这样的,另一方面出题人也不大会卡。复杂度是 \(n\) 次 spfa。
但经过观察可以发现,每次保留上一轮的 dis,加入这一轮新加的边迭代,答案是对的。因为加入的顺序不影响答案。这样就可以做到 \(O(nv\log v)\) 了。
「HNOI/AHOI2018」排列(建图 + 贪心)
连边 \(a_i\to i\),有环则无解,否则一定是森林。
根据排序不等式,小的要先选,然后依次合并父亲,结论:平均值小的先选。具体见洛谷题解区。
Code:https://loj.ac/s/1494525。
「AGC056C」01 Balanced(差分约束)
题意:
构造一个字典序最小的长度为 \(n\) 的
01序列,满足 \(m\) 个区间 \([l_i,r_i]\) 中0和1的个数相等。\(2\le n\le 10^6,1\le m\le200000,(r_i-l_i+1)\equiv 0\pmod 2\)。
给 0 赋上 \(-1\) 的权值,1 赋上 \(1\) 的权值,设 \(s_i\) 为 \(1\sim i\) 的权值和。
那么有以下两个等式:
- \(|s_i-s_{i-1}|\le 1\);
- \(s_{l_i-1}=s_{r_i}\)。
这样建图直接跑差分约束要 SPFA,复杂度会假。
第一个可以转化成 \(s_i-s_{i-1}\le 1\) 和 \(s_{i-1}-s_i\le 1\) 两个式子,那么就没有负权边了,可以跑 Dijkstra。
由于边权都是 0/1,所以还可以 01BFS。
Code:https://atcoder.jp/contests/agc056/submissions/32834090。
「省选联考 2021 A/B 卷」图函数(Floyd)
首先把删边倒过来改成加边。
不难发现这个限制条件就是 \(i\) 和 \(j\) 能只通过编号 \(\ge\min(i,j)\) 的点能够相互到达。
由于加边后已经满足条件的不会不满足,所以一对点贡献的是一段前缀,可以差分。
那么我们可以一遍 Floyd 算出 \(g_{i,j}\) 为只经过编号 \(\ge\min(i,j)\) 的点最早可以 \(i\) 到达 \(j\) 的时间。
注意倒过来枚举中转点 \(k\),因为 \(k\) 越大的能贡献到的点对越多;还要满足 \(i\) 和 \(j\) 至少有一个 \(<k\)。
最后,点对 \((i,j)\) 能贡献的前缀就是 \([1,\min(g_{i,j},g_{j,i})]\)。
Code:https://loj.ac/s/1494918。
6.30
「省选联考 2021 A/B 卷」滚榜(状压 dp + 费用提前计算)
首先一看 \(n\) 这么小就是状压。
考场上想到枚举最终顺序,计算出所需要的最少 \(\sum b_i\),这样有 \(60\) 分。
考虑一个最终排名顺序 \(a_1,a_2,\dots,a_n\) 的最少 \(\sum b_i\)。
不难发现,若 \(a_i>a_{i-1}\),那么为了满足 \(b_i\) 不降,就要 \(b_i=b_{i-1}\);否则 \(b_i=b_{i-1}+a_{i-1}-a_i\)。
利用费用提前计算的思想,当前 \(a_{i-1}-a_i\) 会对总的 \(\sum b_i\) 产生 \(n-i+1\) 次贡献。
那么设 \(f_{i,j,k}\) 表示集合 \(i\) 中的数已经在最终排名中被考虑,已经考虑的最后一个数是 \(j\),当前 \(\sum b=k\),可能的排名情况数量。
转移考虑下一个是谁,看费用是否满足 \(\le m\),答案就是 \(\sum f_{2^n-1,i,j}\)。
Code:https://loj.ac/s/1495102。
「六省联考 2017」期末考试(枚举 + 前缀和)
枚举所有课程最后公布成绩的时间 \(i\),用几个前缀和数组分别记录 \(i\) 之前的 \(t\) 的数量、\(\sum t\)、\(b\) 的数量、\(\sum b\)。
另外记录 \(tot=\sum\limits_{i=1}^m b_i\),这样就可以直接算出 \(i\) 之后的 \(\sum b\) 了。
如果 \(A<B\),那么先把之前的用 \(A\) 榨干,还有多的再用 \(B\);如果 \(A\ge B\),那么直接全部用 \(B\) 就行。
应该算是比较水的一道题。
Code:https://loj.ac/s/1495219。
「六省联考 2017」分手是祝愿(期望 dp)
不难发现每次从大到小操作是最优的,具体证明可见洛谷题解区。
设 \(f_i\) 表示还有 \(i\) 个灯要操作的局面,到还有 \(i-1\) 个灯要操作的局面的期望操作次数。
转移:\(f_i=\frac{i}{n}+\frac{n-i}{n}\times(f_{i+1}+f_i+1)\)。
解释一下就是有 \(\frac{i}{n}\) 的概率按灭一个,有 \(\frac{n-i}{n}\) 的概率再按亮一个,那么还需要 \(f_{i+1}+f_i\) 次再按灭两个。
化简一下得到 \(f_i=\frac{n+(n-i)f_{i+1}}{i}\)。
怎么计算答案呢?如果一开始所需要的次数就 \(\le k\) 那么直接输出即可;否则所需次数就是 \(k+\sum\limits_{i=k+1}^{cnt}f_i\)。
Code:https://loj.ac/s/1495373。
「APIO2020」交换城市(Kruskal 重构树)
最大边权最小,马上想到 Kruskal 重构树。
思考一下这个条件怎么用,发现它就是说一条路径上至少有一个点度数 \(>2\) / 成环,那么在 Kruskal 的时候顺便维护一个 \(vis\) 标记即可。
询问是求出两个点的 LCA,答案就是 LCA 及其祖先 \(vis=1\) 的权值的最小值。
参考题解:https://moyujiang.blog.luogu.org/solutionp6765。
Code:https://loj.ac/s/1495591。
「SHOI2014」概率充电器(换根概率 dp)
比较阴间的换根 dp。
首先这题是一个假期望,因为权值都是 \(1\),实际上是求概率之和。
一个点贡献有 \(3\) 种方式:
- 自己给自己充电;
- 子节点给它充电;
- 父节点给它充电。
前两种都比较好求:先初始化 \(dp_i=q_i\),然后一遍从上到下 dfs 计算 \(dp_i\leftarrow dp_i+(1-dp_i)\times dp_{son_i}\times p_{i,son_i}\)。
考虑第 \(3\) 种情况就要用到换根了。
注意到还是那个式子,但是 \(dp_{fa_i}\) 有一部分已经是 \(dp_i\) 转移过去的,这部分要去掉。
\(dp_{fa_i}=dp_{fa_i}'+(1-dp_{fa_i}')\times dp_i\times p_{i,fa_i}\),其中 \(dp'_{fa_i}\) 就是去掉 \(dp_i\) 贡献的概率。
化简得 \(dp'_{fa_i}=\frac{dp_{fa_i}-f_i\times p_{i,fa_i}}{1-f_i\times p_{i,fa_i}}\)。
Code:https://loj.ac/s/1495888。
「CF1039D」You Are Given a Tree(根号分治 + 树形 dp)
很明显,当 \(k\ge \sqrt n\) 时,答案 \(\le \sqrt n\)。
那么我们可以暴力做 \(k<\sqrt n\) 的部分,剩下的和数论分块一样二分一个答案相同的右端点就行了。
一遍 \(O(n)\) 求答案可以类似求树的直径一样 dp,每次上传最长链,如果最长 + 次长 \(\ge len\),答案就 \(+1\),否则就传最长的链上去。
注意为了卡常,我们可以把 dfs 序求出来后循环做树形 dp。
Code:https://pastebin.ubuntu.com/p/C4JhJMYFMR/。
7.1
2022.7.1 考试 T1 卢瑞恩(lurien)(单调栈 + 长链剖分 + 贪心)
题面:
你的面前有一排 \(N\) 个炸弹,从左到右编号为 \(1 \sim N\),第 \(i\) 个炸弹预计在 \(t_i\) 秒爆炸。爆炸还会造成连锁反应,具体地,如果炸弹 \(i(i < n)\) 在第 \(t\) 秒爆炸,且炸弹 \(i + 1\) 还未爆炸,则后者会在 \(t + 1\) 时刻爆炸(并有可能继续引发后续的连锁反应)。
你现在有 \(D\) 个隔板,隔板可以阻断炸弹之间的连锁反应。你希望在 \(T\) 秒之前(包括第 \(T\) 秒)爆炸的炸弹最少,请输出这个最小值。
数据范围:\(1 \le D < N \le 2 \times 10^6 ,1 \le T, t_i \le 10^9\)。
对每一个炸弹 \(i\) 考虑最靠近它的一个炸弹 \(j\) 使得 \(t_j+i-j\le T\),即若离 \(i\) 最近的隔板在 \(j\) 之前,那么 \(i\) 会爆炸。
记 \(p_i=j\),那么 \(p_i\) 能用单调栈求出。
不难发现所有 \([p_i,i)\) 的区间要么包含,要么不交,即形成了一个树形结构。在单调栈的时候可以建出树。
问题转化成选 \(D\) 个叶子使得链并最大,长链剖分后选前 \(D\) 大即可。
Code:https://pastebin.ubuntu.com/p/b992Mmy7Sy/。
2022.7.1 考试 T3 赫拉(herrah)(dp)
题面:
给定一个长度为 \(n\) 的
01序列,其中有些位置的值已经确定,剩余部分01等概率出现。对最终序列求最长不下降子序列(如有多个取
1最多的那个),设最长不下降子序列长度是 \(a\),最长不下降子序列中1的个数是 \(b\),那么这个序列的权值就是 \(a \times b\)。求这个序列权值的期望,对 \(10^9 + 7\) 取模。
数据范围:\(1\le n\le 1000\)。
对于每一个已经确定的 01 序列,考虑怎么求 \(a\) 和 \(b\)。
画一个折线图,从 \((0,0)\) 出发,遇到 0 往右下走,遇到 1 往右上走。
下图是 0110001110001 的折线图。

我们发现在选择 最低点左边的 0 和 最低点右边的 1 可以取到不降子序列长度的最大值。(这个图的最低
点时 G 和 M)
而同时要求 1 尽可能多,我们取最左边的最低点即可。(所以最低点取 G)
考虑前 \(i\) 个 01,当前位置与之前的最低点相差 \(j\) 时,所有方案中 \(1\) 的和、\(a\) 的和、\(b\) 的和与 \(a\times b\) 的和即可。
Code:https://pastebin.ubuntu.com/p/z7xYz8VXhz/。
7.2
2022.7.2 考试 T1 脊椎(spine)/「ARC117E」Zero-Sum Ranges 2(dp)
题意:
问有多少个长度为 \(2n\) 的
01序列满足:
- 序列中恰有 \(n\) 个
0和 \(n\) 个1;- 序列中恰有 \(k\) 个子段满足子段内
0和1数量相等。答案对 \(10^9+7\) 取模。
数据范围:\(1\le n\le 50,0\le k\le n^2\)。
把 0 看成 \(-1\),1 看成 \(1\),做一个前缀和,那么 01 数量相等的子段数量就是 \(\sum \binom{cnt_i}{2}\),其中 \(cnt_i\) 是前缀和为 \(i\) 的位置个数。
考虑前缀和是一些层,按照层从小到大 dp。两个数之间会有一些空要填上,那么就可以设 \(dp_{i,j,k}\) 表示已经填了 \(i\) 个数,总贡献是 \(j\),还有 \(j\) 个空要填的方案数。转移直接枚举下一层填 \(l\) 个数,系数可以根据插板法得出是 \(\binom{l-1}{k+1}\)。
最后答案还要把 \(<0\) 和 \(>0\) 两部分拼起来。
参考:https://blog.csdn.net/Z_Y_S_/article/details/122986958。
代码:https://pastebin.ubuntu.com/p/2bybb22hRG/。
2022.7.2 考试 T2 翻译(identify)(AC 自动机 + 贪心)
题意:
有一个文本串 \(s\) 和 \(m\) 个字符串。你需要对于每一个字符串找出一个非空前缀,使得对于第 \(i\) 个字符串 \(t_i\),设你找出的前缀是 \(p_i\),则 \(p_i\) 在 \(s\) 中应当出现 \(\ge k_i\) 次,且 \(p_i\) 之间必须两两不同。
显然无解是有可能的,因此你可以删掉 \(m\) 个字符串中的一些。问最少删除多少个字符串使得能够找到合法解。特别地,规定删除所有字符串之后可以找到合法解。
数据范围:\(1\le |s|,m,\sum t_i\le 5\times 10^5\),\(1\le k_i\le |s|\),字符集为小写字母。
建出 AC 自动机与 fail 树,那么可以求出最长的一个前缀满足这个条件。
然后选最长的加进 map,不难发现这样贪心没有问题。
Code:https://pastebin.ubuntu.com/p/7Ksmh8mjtf/。
7.4
「CF1408G」Clusterization Counting(Kruskal 重构树 + 树形背包)
结论:按边权从小到大加边,一个连通块能算成一个组当且仅当它是一个团。
有了这个结论后,建出 Kruskal 重构树,一个子树合法就打上标记,然后就是一个树形背包的事了。
Code:https://pastebin.ubuntu.com/p/mQ2YW7V5MN/。
「洛谷 P4704」太极剑(贪心)
断环成链后,不难发现一根绳子变成了三个区间。
将问题转化为选最少的断点使得每个区间内外都至少有一个断点。
枚举长度最小的区间,那么它里面一定有一个断点。枚举位置,然后向后扫,如果一个右端点对应的左端点在最近的断点之后,那么新开一个断点在右端点。答案就是 \(\lceil\frac{cnt}{2}\rceil\)。
Code:https://pastebin.ubuntu.com/p/xcvPxVGZzW/。
「HNOI2007」梦幻岛宝珠(dp + 性质)
直接背包肯定不大行,根据题目给出的 \(a\le 10,b\le 30\) 可以动动手脚。
把每一个 \(b\) 分开考虑,设 \(f_{i,j}\) 表示考虑 \(b=i\) 时,\(\sum a=j\) 的最大价值。这个可以直接 01 背包。
再设一个 \(g_{i,j}\) 表示考虑到 \(b=i\),\(b=i\) 的 \(\sum a=j\),其它的用了 \(m\) 的二进制表示下后 \(i-1\) 位的最大价值。
这个转移可以枚举拆出来 \(k\) 给剩下的物品放,式子就是:
\(2k\) 是因为 \(k\times 2^i=2k\times 2^{i-1}\)。
答案就是 \(g_{m的位数,1}\)。
Code:https://pastebin.ubuntu.com/p/KDNq3mzvWr/。
「SDOI2019」快速查询(打标记)
先把所有要查询 / 赋值的数拿出来,然后再进行操作。
维护全局标记 \((pls,mul)\),表示 \(a_i=mul\times val_i+pls\)。
除了全局赋值都可以很好地维护。
全局赋值其实可以暴力把已经赋值的改掉,复杂度是 \(\sum\) 的。
Code:https://pastebin.ubuntu.com/p/SDfdzNfMW4/。
7.5
「AGC002D」Stamp Rally(整体二分 + 并查集)
最大编号最小 \(\to\) 二分。多次询问 \(\to\) 整体二分。
其实判断就是看两个点的连通块大小之和有没有达到 \(z\)。
并查集撤销的时候注意是撤销到进入这一层之前的位置。
Code:https://pastebin.ubuntu.com/p/sST2BNB4b4/。
2022.7.5 考试 T1 Battle For Farbanti(giantstep)(dp 优化状态 + 树状数组)
题意:
给定一个长度为 \(n\) 的排列,求出长度非严格第 \(k\) 大的上升子序列的长度是什么。
数据范围:\(1\le n\le 10^5,1\le k\le 10^{18}\)。
答案肯定大于等于 LIS - \(\log_2 k\),因为每个长度为 \(i\) 的子序列都可以看成长度为 LIS \(=a\) 的上升子序列中选出 \(i\) 个数,即 \(\binom{i}{a}\),而对它们求和是 \(2^i\) 级别的。
那么设 \(dp_{i,j}\) 表示 \(1\sim i\) 个数,以 \(i\) 结尾的长度为 \(j\) 的上升子序列个数。
可以预处理出 \(p_i\) 表示 \(1\sim i\) 的 LIS 长度,那么合法的状态只为 \(dp_{i,p_i-\log_2 k}\sim dp_{i,p_i}\)。
然后树状数组优化即可。
Code:https://pastebin.ubuntu.com/p/9GqFwyZmyS/。
2022.7.5 考试 T2 Last Hope(gorgon)/「CF1667D」Edge Elimination(构造)
这种树上删边类型的问题可以类似「CSP-S 2019」树上的数那样考虑,即把每个点与它相连的边单独拿出来看成一个菊花图,在菊花图上面钦定顺序,然后用拓扑排序确定相对顺序。
我们对每个点连边的删边确定顺序,偶数标成 0,奇数标成 1(即被删的相对顺序的奇偶性)。那么肯定 1 的个数为 \(\lfloor\frac{du_i}{2}\rfloor\)。这样就可以判定合不合法。
不难发现肯定是 0101... 这样的形式,我们就钦定一个顺序。这个可以从叶子往上确定。
碰到度数为偶数的情况,我们应该排成 1010...,因为 1 的另一端点有奇数条边,这边也应该有奇数条。
将边看成点,对于钦定的顺序,我们将前一条边对应的点向后一条边对应的点连一条边。
不难发现肯定不会有环,拓扑排序即可。
Code:https://pastebin.ubuntu.com/p/zfcG2FJpyn/。
2022.7.5 考试 T3 Homeward(reflux)/「AGC119F」AtCoder Express 3(dp)
题意:
数轴上有 \(n\) 个点,从 \(0\) 开始编号。初始在每个 \(i\) 和 \(i+1\) 两点之间有一条长度为 \(1\) 的边。
每个点可以被划分为两个类别,A 类和 B 类,特殊地,第 \(0\) 个点和第 \(n\) 个点同时属于两类。
每个编号相邻的同类点都会连一条长度为 \(1\) 的新边。
有一些点的分类已经确定,有一些没有确定,你需要求出有多少种分类方案满足最后图中 \(0\) 到 \(n\) 的距离不超过 \(k\),对 \(10^9+7\) 取模。
数据范围:\(1\le n\le 4000,1\le k\le \frac{n+1}{2}\)。
dp 好题。
先考虑确定了每个点的类别怎么算最短路。
先不考虑往回走的情况,从小到大枚举每个点,设 \((\alpha,\beta)\) 为 \(0\) 到最后一个 A 类的最短距离和 \(0\) 到最后一个 B 类的最短距离。
初始 \((\alpha,\beta)=(0,0)\)。讨论 \(c_{i-1}\) 和 \(c_i\) 的类别。
- 若 \(c_{i-1}=\)
A,\(c_i=\)A,则 \(\alpha\leftarrow \alpha + 1\); - 若 \(c_{i-1}=\)
B,\(c_i=\)A,则 \(\alpha\leftarrow \min(\alpha,\beta)+1\); - 若 \(c_{i-1}=\)
A,\(c_i=\)B,则 \(\beta\leftarrow \min(\alpha,\beta) + 1\); - 若 \(c_{i-1}=\)
B,\(c_i=\)B,则 \(\beta\leftarrow \beta + 1\)。
注意到往回走一定是沿着初始的边,且一次最多往回走一步,因为否则路径中就会出现环。

所以我们可以用下面的方式求最短路:
- 若 \(c_{i-1}=\)
A,\(c_i=\)A,则 \(\alpha\leftarrow \alpha + 1\); - 若 \(c_{i-1}=\)
B,\(c_i=\)A,则先更新 \(\alpha\leftarrow \min(\alpha,\beta)+1\),再更新 \(\beta=\min(\alpha+1,\beta)\); - 若 \(c_{i-1}=\)
A,\(c_i=\)B,则先更新 \(\beta\leftarrow \min(\alpha,\beta) + 1\),再更新 \(\alpha=\min(\alpha,\beta+1)\); - 若 \(c_{i-1}=\)
B,\(c_i=\)B,则 \(\beta\leftarrow \beta + 1\)。
由此可以得到一个 \(O(n^3)\) 的 dp。
设 \(f_{i,j,k,0/1}\) 表示枚举到第 \(i\) 个点,\(\alpha=j\),\(\beta=k\),当前点是 A 类还是 B 类的方案数。
- 若 \(c_{i+1}\not =\)
B,则有转移:- \((i,j,k,0)\to(i+1,j+1,k,0)\);
- \((i,j,k,1)\to (i+1,\min(j,k)+1,\min(k,\min(j,k)+1+1),1)\)。
- 若 \(c_{i+1}\not=\)
A,则有转移:- \((i,j,k,1)\to(i+1,j,k+1,1)\);
- \((i,j,k,0)\to(i+1,\min(j,\min(j,k)+1+1),\min(j,k)+1,1)\)。
最终答案就是所有满足 \(\min(j,k)\le K\) 的 \(dp_{n,j,k,0/1}\) 之和。
我们发现,当 \(\alpha\) 和 \(\beta\) 差很多的时候我们并不需要记录较大者的值,因为可以在下一次 AB 两种点相邻时
用较小者来更新较大者。

例如上图这种情况,我们把它替换成下图的情况最短路不会受到影响。

所以当 \(\alpha-\beta>2\) 时,我们可以当 \(\alpha=\beta+2\) 来做,具体的,在转移的时候将 \(\alpha\) 对 \(\beta+2\) 取 \(\min\) 即可。反之亦然。
这样 DP 中只需要记录两维即可,复杂度 \(O(n^2)\)。
Code:https://pastebin.ubuntu.com/p/RPMT2sF6ZX/。
7.6
「CF555E」Case of Computer Network(边双连通分量 + 树上差分)
求出边双后缩点成一棵树,很明显边双内的点都能通过构造相互到达。
那么就变成一些边一定要向上,一些边一定要向下,可以求出 LCA 后树上差分。
如果一条边既要向上,又要向下,那么就无解。
Code:https://pastebin.ubuntu.com/p/dXG69KtWyC/。
「CF512D」Fox And Travelling(拓扑排序 + 树形背包)
发现环上的点肯定不会选,那么拓扑排序去掉环后会形成若干棵树,对这些树分开考虑后合并答案。
如果树接在环上,那么我们把它当作有根树来看,只要做一次树上背包;否则就以每个点为根做一次树形背包,不难发现一个选 \(i\) 个点的方案被统计了 \(size-i\) 次(因为在其它的所有点都会统计一次)。
那么对于每一棵树做一次树形背包,最后把所有树的背包合并就行。
Code:https://pastebin.ubuntu.com/p/zS5sMm7R8Y/。
7.7
「CF1292F」Nora's Toy Boxes(状压 dp)
懒得写题解了,挂个链接:https://www.luogu.com.cn/blog/Rainbowsjy/cf1292f。
Code:https://pastebin.ubuntu.com/p/tnd6DNqc7N/。
「CF1286E」Fedya the Potter Strikes Back(KMP + 二分 + 单调栈)
每次向后增加一个字符,不难想到考虑新增加区间的贡献。
如果一个区间 \([l,r]\) 合法,那么它一定是 \([1,r]\) 的一个 \(\bf{border}\),由此可以推出合法的区间都是失配树上当前节点的祖先。
但是失配树上节点的权值在变化,不好直接维护,我们考虑维护 \(\bf{border}\) 集合。
加入一个字符 \(c\) 后:
- 若 \(c=s_1\),则加入一个长度为 \(1\) 的 \(\bf{border}\);
- 对于一个长度为 \(x\) 的 \(\bf{border}\),若 \(s_{x+1}\not = c\),那么就删除这个 \(\bf{border}\)。
因为最多加入 \(\mathcal{O}(n)\) 次,所以只要我们可以快速删除不合法的情况就能够做到 \(\mathcal{O}(n)\) 维护 \(\bf{border}\) 集合。
怎么快速删除?我们对于失配树上的每一个节点 \(i\),记 \(f_i\) 表示离 \(i\) 最近的祖先满足后继字符不等于 \(s_{i+1}\),即设 \(f_i\) 的 \(\bf{border}\) 长度为 \(x\),那么 \(s_{x+1}\not= c\)。这个可以每次 \(\mathcal{O}(1)\) 求。
那么我们在删除的时候,如果发现当前节点 \(x\) 满足 \(s_{x+1} = c\),那么我们直接跳到 \(f_i\) 就可以实现 \(\mathcal{O}(1)\) 找到下一个需要删掉的节点。
接下来就是维护权值的问题,我们要支持加入、删除、全局取 \(\min\)、求和的操作。
可以开一个 map 记录权值,并且维护一个权值递增的单调栈,每次删除直接在里面二分找到权值;取 \(\min\) 可以暴力做,复杂度是均摊 \(\mathcal{O}(\log n)\) 的。做这些操作的过程中顺便维护权值和就行。
注意要用 __int128。
Code:https://pastebin.ubuntu.com/p/YM9MWKpSGj/。
「CF1684H」Hard Cut(构造)
神秘构造题,枚举最终是 \(2^k\),如果再划分一段超过了 \(2^k\) 就在这个点 cut 一下。我也不知道为什么是对的。
Code:https://pastebin.ubuntu.com/p/PNNYgt3szP/。
「AT3913」「APC001」XOR Tree(转换 + 状压 dp)
容易想到把每个点点权看成它到根的路径的权值异或和,但这样没啥用,因为一次修改还是会改很多个点。
考虑链的一些性质,其中有一条是有 \(len-2\) 个点度数为 \(2\),剩下 \(2\) 个点度数为 \(1\),考虑在这上面做文章。
一种想法是把每个点的点权设成与它相连的边的边权异或和,这样改一条链就只会改变两个端点了。
把所有点的点权拿出来,每次可以先把两个一样的删掉。因为边权 \(\le 15\),所以可以直接暴力统计。
对于剩下的有奇数个点有的点权,可以状压。设 \(f_S\) 表示有 \(S\) 中的数,要使得异或和为 \(0\) 的最少操作次数。
显然若 \(S\) 中的数异或和不是 \(0\) 就不可能变成 \(0\),否则下界就是 \(\operatorname{popcount}(S)-1\)。DP 的时候枚举一个异或和为 \(0\) 的子集进行转移。
Code:https://pastebin.ubuntu.com/p/xr2Bfbt62v/。
7.8
「ONTAK2010」Peaks 加强版(Kruskal 重构树 + 倍增 + 主席树)
将边权从小到大排序,建出 Kruskal 重构树,询问时倍增找到权值 \(\le x\) 的深度最浅的点。
第 \(k\) 大可以主席树维护,具体的就是算出每个点子树内 dfs 序区间,把子树问题变成区间问题。
一定要弄清楚下标的具体含义。
Code:https://pastebin.ubuntu.com/p/hFDBSPP4b2/。
「yLOI2020」泸沽寻梦(unordered_map + 异或的性质)
区间异或改成前缀异或,容易发现每次修改只会改一个位置的值,那么开一个 unordered_map 记录每个值的个数并且顺便计算 \(\sum\binom{cnt}{2}\) 就行。
Code:https://pastebin.ubuntu.com/p/FHbbzYCWsN/。
「TJOI2017」异或和(异或的性质 + 树状数组)
考虑按位做,看这一位为 \(1\) 的区间和的个数的奇偶性。
考虑怎么算 \(\oplus_j(s_i-s_j)\)。手玩一下发现,如果 \(s_i\) 与 \(s_j\) 第 \(k\) 位不同,那么只有当 \(s_i\) 的后 \(k-1\) 位 \(\ge s_j\) 的后 \(k-1\) 位时这一位才为 \(1\);否则就要求 \(s_i\) 的后 \(k-1\) 位 \(< s_j\) 的后 \(k-1\) 位。
那么开一棵值域树状数组就行。
Code:https://pastebin.ubuntu.com/p/VBBTyCMVZy/。
「CF981D」Bookshelves(贪心 + 按位 + dp)
不难发现位越高越好,所以从高到低枚举每一位。
判断可以用简单 dp:设 \(f_{i,j}\) 表示前 \(i\) 本书分成 \(j\) 个书架,能不能在之前答案的基础上加上这一位的答案。直接枚举转移。
Code:https://pastebin.ubuntu.com/p/6ZRXdC3wfs/。
7.9
2022.7.9 考试 T1 马格利特(Magritte)(构造)
题意:
给一个长度为 \(n\) 的序列 \(\{a\}\)(\(n\) 为偶数),每次可以选择两个数 \(i\)、\(j\),将 \(a_i\)、\(a_j\) 都替换成 \(a_i\times a_j\)。构造一个在 \(10^6\) 步以内的方案使得 \(a_1=a_2=\dots=a_n\)。
数据范围:\(2\le n\le40000\),\(n\) 为偶数,\(1\le a_i\le 10^6\)。
\(n\le4\) 的情况是平凡的,当 \(n>4\) 时,考虑递归地构造。
先将前 \(n-2\) 个数递归下去,变成一样。再将 \(n-1,n\) 变成一样,此时序列变为 \(\underbrace{a,a,\cdots,a}_{n-2个},b,b\)。
接着进行以下操作(\(\underline{a}~,~\underline{b}\) 表示下一步操作 \(a\) 和 \(b\)):
最后将前 \(n-4\) 个数第 \(i\) 个与第 \(n-3-i\) 个配对,所有数变为 \(a^{n-3}b^2\)。
直接这样做操作次数是 \(O(n^2)\) 的,可以进行一些优化。比如当 \(\dfrac n2\) 还是偶数时,可以将序列左右两半分别操作后合并起来,这样操作次数就是 \(O(n\log n)\) 了。

浙公网安备 33010602011771号