做题记录
用来记录在做题时遇到的一些有意思/看了题解才做出来的题。
Reachability from the Capital
首先,如果一个点本来就可以被 \(s\) 点到达,那么可以直接在图中删去。
考虑剩下的点。
我们对每个点求出其能到达那些点,然后按能到达的点的数量从大到小排序,再依次删去这些点(每次删完要把这个点能到达的点删去,因为其已经和 \(s\) 联通),统计答案。
删去一个点不用真的把它干掉,打个标记即可。
Tree with Small Distances
又是一道不会做的图(树)上问题,看来找时间得补补。
考虑贪心的去选择一个点连边。
很自然的可以想到选择离根最远的那个点,然后连边。
但思考后可以发现这样并不是最优的,如图。

红色节点是我们要连接的点。
如果我们连接了 \(rt\) 和红色节点,那么我们就要连两条边,方案如下(蓝色是新连的边):

然而可以发现,如果我们连接红色节点的父亲节点显然只要一条边。
那么我们就可以找到一个离红色节点最远的点,使得这个点与红色节点的距离不超过 \(1\)(因为到达那个节点还要走一条边)。
那么就有两种方法连接,要么连接其父亲节点,要么连接儿子节点。
但由于我们是按深度从大到小选择,所以其儿子一定是合法的了。
所以我们直接连接父亲节点就好了。
在连接完节点后要把其和与其相邻的点标记,不再操作(因为这些点已经合法)。
Fake Plastic Trees
干脆改成图树选讲得了
对于每一个点,显然有一种最优的策略,那就是我们让这个点的 \(a\) 值最大(前提条件是操作次数要最少)。
因为这样能给其祖先节点带来更多的选择方式。
那么考虑一个点其能获得的在操作次数最少的情况下是多少。
首先,其子节点的 \(a\) 的和是肯定可以得到的,然后分类讨论一下两种情况:
-
\(a_u<l_u\):对于这种情况,显然需要对该节点额外进行一次操作,那么答案增加,且将 \(a_u\) 改为 \(r_u\)(反正都花代价了,为什么不选一个最好的呢?)
-
\(l_u\le a_u\):对于这种情况,我们需要判断其值有没有超过 \(r_u\) 如果超过了就设为 \(r_u\)。
水の数列
一个很妙的题,还是写一下吧。
(由于该问题需要化简,所以就从题目背景中的题来讲吧)
问题 1:
贪心水题,题目中有做法了。
问题 2:
考虑到值域只有 \(10^6\),那么枚举 \(x\) 然后暴力给区间打标记。
但这样就变成 \(10^6n\) 了(离散化后是 \(n^2\),但 \(n\) 有 \(10^6\) 没影响)。
考虑如何快速的维护区间。
不难想到,我们从小往大枚举 \(x\) ,然后把与 \(x\) 相同的数在序列中标记,那么每次我们都会加入一些数。
然后用并查集维护,将区间看作一个集合。
因为要求平方和,所以还要维护并查集每个节点的子树大小。
每次合并计算代价。
代价的增量(\(d\) 数组存的是子树大小,\(x\) 是合并后的父亲节点,\(y\) 是合并后的子节点):
虽然没必要化简但还是化简一下吧
时间复杂度:\(\operatorname{O}(V\log n)\)(当然可以加个按秩合并)
问题 3:
我们在并查集的过程中另外用一个变量计算段数,然后找到段数在 \(l\) 到 \(r\) 中的最大值即可(具体维护方法就是每次将一个数标记为可行就加 1,合并两个集合就减 1)。
时间复杂度:\(\operatorname{O}(V\log n)\)
问题 4:
有 \(T\) 组询问,所以开桶把每个段数的最大答案存下来。
然后维护区间 RMQ。(由于要输出方案,所以要把 \(x\) 与代价捆绑)
用 ST 表 / 线段树会被卡空间。
由于 \(T\) 很小,直接用莫队或分块。
时间复杂度:\(\operatorname{O}(V\log n+T\sqrt{V})\)
问题 5:
不能离线了,所以不能用莫队。
但还是可以用分块。
时间复杂度:\(\operatorname{O}(V\log n+T\sqrt{V})\)
[yLOI2019] 梅深不见冬
贪心好题。
考虑对于每个节点计算代价。
有个显然的贪心策略:当前节点的子节点在当前节点放了数时就可以全部取消掉了。
那么我们就得到了答案的第一种来源:当前节点的权值加上其所有子节点的权值。
然后考虑最大值出现在给子节点放数的时候。
由于前面放的子节点的数当前还不能取消,所以第 \(i\) 个儿子在放置过程中的最高代价是:
然后我们考虑如何安排子节点的顺序。
假设我们已经确定了前 \(i\) 个节点的顺序,考虑接下来 \(x\) 和 \(y\) 那个节点更优。
这样不好分析,所以我们设 \(x\) 要优于 \(y\) (很多题目都是这样证明的)。
令 \(sum=\sum_{j=1}^{j<i}a_{id_j}\),那么当 \(x\) 优于 \(y\) 时,以下不等式成立:
所以我们把子节点按照 \(ans_x-a_x\) 从大到小排序,依次计算即可。
[六省联考 2017] 组合数问题
很好的矩阵乘法题。
首先众所周知组合数是可以用递推来求的。
设 \(f_{i,j}=C_i^j\),则有:
边界条件为 \(f_{i,0}=1 (1\le i\le n)\)。
这题中要我们求的是\(\sum_{i = 0}^\infty C_{nk}^{ik + r}\),所以相当于求 \(\sum_{i = 0 \wedge i \equiv r(mod _k)}^{nk} C_{nk}^{i}\),所以设 \(f_{i,j}\) 表示 \(\sum_{s = 0 \wedge s \equiv j(mod _k)}^{i} C_{i}^{s}\),则有:
那么答案就是 \(f_{nk,r}\)。
但是考虑到 \(n\) 很大,直接递推显然超时,但 \(k\) 和 \(r\) 很小,所以直接矩阵乘法优化一下就好了。
为了方便,可以把矩阵的下标从 \(0\le i<k\) 偏移成 \(1\le i\le k\)。
Squirrel Migration
首先有一个 trick:如果要让距离和最大,那么以重心为根,每个点都只会往重心的另外一个子树中的点匹配。
证明:
- 每个点是否可向外匹配:
根据重心的定义,每个子树大小都是小于等于 \(\frac{n}{2}\) 的,所以除了该子树中的点剩下的点数量是大于等于 \(\frac{n}{2}\),所以一定是可行的。
2.如果不这样进行匹配是否不会更劣
如果有点向同一个子树内的点匹配,可以让每对中的一个点向另外一对中的一个点匹配,这样答案至少会增加 \(4\),所以不这样匹配一定更劣。
那么我们就可以把所有子树的大小拿出来做 dp 了。
直接 dp 显然不好写,考虑容斥。
设 \(f_{i,j}\) 表示前 \(i\) 个子树有 \(j\) 个点向自己子树内匹配,那么有转移方程:
其中 \(a_i\) 是当前子树大小。
那么根据二项式反演,答案就是:
其中 \(c\) 是子树个数。
Sequence of Substrings
看到这题首先有个很显然的 dp:\(f_{i,j}\) 表示当前最后一个区间为 \([i,j]\),最多选了多少区间。那么有转移:
这样是 \(\operatorname{O}(n^4)\) 的,无法通过。
我们先不考虑优化复杂度,而是先优化一下 dp 的方式。
我们可以把所有串都拿出来,然后按照字典序排序。这样对于每个字符串,我们找到其前面一个不等于它的串,并且这个串在原串的位置也要在它前面,然后进行转移。这时我们可以用线段树维护每个串结尾的位置对应的 dp 最大值,优化转移。
此时转移的复杂度已经较低了(虽然仍然无法通过),我们考虑优化这个排序。
排序本身很难优化(最多也只能用二分+hash优化到 \(\operatorname{O}(n^2log^2n)\)),所以我们考虑用某些东西替代排序。
不难想到我们可以用字典树!(下文中该节点对应字符串是指从根节点到当前节点路径上的字符连接)由于是 01 串,所以只有两个儿子。画个图不难发现该节点对应字符串字典序要小于左儿子的字符串的字典序,而左儿子对应字符串字典序要小于右儿子的字符串的字典序,所以我们先对当前点进行转移,然后递归处理左儿子,最后处理右儿子。
现在时间复杂度是 \(\operatorname{O}(n^2\log n)\) 的了,但依旧无法通过,原因是我们现在字符串的个数就有 \(\operatorname{O}(n^2)\) 个。考虑如何让字符串个数减少。
结论 1:如果选择的后一个字符串比当前结尾的字符串长,那么其长度与当前结尾的字符串的长度之差不会超过 1
证明:
考虑如果有一个字符串长度比前一个字符串多 2 会怎么样。
不难发现,此时我们将这一个字符串的最后一个字符删掉该字符串也是比前一个字符串的字典序大的,而这样显然不会让答案变小(甚至可能变大)。
结论 2:一定有一种合法的选择方法使得答案最大且选择的字符串的长度均小于等于 \(\sqrt{2n}\)。
证明:
在结论1的条件下,如果我们想要最长的串长度最大,那么长度选出的串的长度肯定是 \(1,2,3...... x\) 每次加一,那么字符串的长度和就是 \(\frac{x\times (x+1)}{2}\),由于字串之间不能相交,所以 \(x\le \sqrt{2n}\)。
有了结论 2,我们就有了一个很强的优化:枚举长度小于等于 \(\sqrt{2n}\) 的字符串,然后进行 dp。这样时间复杂度是 \(\operatorname{O}(n\sqrt{n}\log n)\) 的,瓶颈在于 dp 转移时的线段树优化。
CF1537F
一道结论型的图论题。
约定:
偶环:节点个数为偶数的环使得任意不相同两点之间有且仅有 2 条简单路径的环。
奇环:节点个数为奇数的环使得任意不相同两点之间有且仅有 2 条简单路径的环。
令点 \(i\) 的权值为 \(a_i\),有 \(a_i=t_i-v_i\),其中 \(v_i,t_i\) 为题目给出的。
称一个图为好的当且仅当这张图是否可以在有限步操作中,使得每个节点满足 \(a_i=0\)。
基础结论:
结论 1:对于一个偶环,这个偶环是好的当且仅当将其黑白染色后所有黑点的权值和等于所有白点的权值和。
证明:
染色后每个黑点想要使自己点权变化那么左右的其中一个白点也会变化,所以黑、白点权值之和的差值不变。要使得最后权值都为 0,那么黑、白点权值之和必须相等。
对于这个结论,还有一个小推广:该结论对于所有二分图都有效,证明过程类似。
结论 2:对于一个奇环,这个奇环是好的当且仅当其点权之和是偶数。
证明:
对于一个奇环,两点间的两条路径长度一定一个是奇数一个是偶数,那么如果有两个点要同时操作,就找到长度为奇数的那条路然后一加一减即可,如图:

这个结论对奇环上挂了树同样适用。
一般性的结论
前面考虑了一个环的情况,现在考虑更一般性的,也就是两个环,或环上挂了树。
两个环有以下三种大致状态:



下文只考虑第三种,事实上这三种不同形态推导结论的方式是类似的。
1. 两个偶环相接
如图:

由于两个偶环接在一起是一个二分图,根据基础结论 1 的小推广,该图可以视作偶环。
2. 偶环与奇环相接
如图:

考虑一种最基础的,这张图上只有两个点的权值为 1。
如果这两个点都在偶环上,那么可以从如下路线一加一减,得到答案:

如果都在奇环上,那么直接就是结论 2。
如果一个在偶环上,一个在奇环上,那么把中间那条边删掉对答案没有影响,所以依旧是结论 2。
综上所述,偶环与奇环相接的情况就相当于奇环。
3. 奇环与奇环相接
如图:

依旧考虑这张图上只有两个点的权值为 1。
如果两个点在同一个奇环上,那么就是结论 2。
否则,如下图:

由于环大小是奇数,所以将中间那条边经过一个奇环但不走中间的边的路径长度是偶数的,而经过中间的边路径长度是奇数(也就是 1),所以这两点之间一定有一条长度为奇数的路径,在这条路径上操作即可,如图:

综上所述,两个奇环相接的情况就相当于奇环。
4. 环上挂树
此时我们只要考虑奇环的情况,因为偶环在此时是二分图。
如图:

我们可以从树的叶子节点开始,不断向上进行修改,将所有权值转移至环上,然后就是结论 2。
综上所述,如果一个图中包含奇环,那么就可以视作奇环,否则视作偶环。
实现
用 Tarjan 求一个生成树,然后对于每条返祖边,看构成的环是否是奇环。或者直接黑白染色判断是否是二分图,如果是二分图就不存在奇环。
[NOIP2022] 建造军营
不难发现,只有割边被炸才会导致两个关键点无法互相到达。所以我们可以直接把边双连通分量缩在一起。
缩完之后这张图就会变成一棵树。
考虑在这棵树上做dp。
定义 \(f_{i,0}\) 表示在 \(i\) 的子树中没有出现关键点,\(f_{i,1}\) 表示在 \(i\) 的子树中出现了至少一个关键点。
但这样很容易重复,所以我们钦定 \((fa,i)\) 这条边一定不选。令 \(c_i\) 表示第 \(i\) 个边双内有多少条边,\(cnt_i\) 表示有多少点,\(s_i\) 表示这棵子树里有多少原图的边(包括边双内的),这样答案就是 \(\sum f[i][1]\times 2^{max(0, m - 1 - s_i)}\)。(因为其它的边随便选不选)。
考虑转移,首先 \(f_{u,0}\) 很好转移,转移如下:
然后考虑 \(f_{u,1}\),对于一个儿子 \(v\),考虑一下两种情况:
- 之前的儿子和 \(u\) 都没有出现关键点
有:\(f_{u,1}=f_{u,0}\times f_{v, 1}\)
- 之前的儿子或 \(u\) 中出现了关键点
有:\(f_{u,1}=f_{u,1}\times (f_{v, 1}+2\times f_{v,0})\)
两种转移合并一下就好了,总时间复杂度为 \(\operatorname{O}(n\log mod)\),预处理逆元可以做到线性。
[CTSC2018] 暴力写挂
令 \(dis(x,y)\) 为 \(x,y\) 的距离,\(dis(x)=dis(1,x)\)。
首先这个答案代价很难计算,所以考虑进行一个式子的推:
推成这样我们就可以开始考虑继续解题了。
首先我们对第一棵树做点分治,令当前根节点为 \(rt\),定义 \(T\) 中 \(v_i=dis(rt)+dis(i,rt)\),那么我们就可以枚举 \(Lca'(x,y)\),然后找到两个 \(x,y\) 使得无论在那一棵树上以当前枚举的点为根它们都不在同一个子树内。
这个问题可以用 dp 解决。设 \(f_{i,0}\) 表示 \(T'\) 这棵树中 \(i\) 子树内最大的 \(v_i\) 对应的 \(i\),\(f_{i,1}\) 表示 \(T'\) 这棵树中与 \(i\) 不在第一棵树的同一个子树中 \(v_i\) 最大的对应的 \(i\)。转移的时候枚举当前的两个状态和当前儿子的两个状态即可。
但这样复杂度是 \(\operatorname{O}(n^2\log n)\) 的,无法通过。
但不难发现,其实我们所有 dp 中有用的点数量加起来是 \(\operatorname{O}(n\log n)\) 级别的,所以可以用虚树对这个过程进行优化。
总时间复杂度 \(\operatorname{O}(n\log^2n)\),瓶颈在于建虚树,不过对 \(dfn\) 排序的时候可以使用 stable_sort降低常数。
Nauuo and Pictures (hard version)
考虑 dp。
设 \(f_{id,i,j}\) 表示对于第 \(id\) 张图片,现在进行了 \(i\) 次操作,其中 \(j\) 次是将某数加一,权值的期望值。 \(\operatorname{O}(1)\) 转移是简单的。
这样时间复杂度是 \(\operatorname{O}(nm^2)\) 的,考虑优化。
考虑将每个照片拆成 \(w_i\) 个同类的且权值为 \(1\) 的照片,那么现在照片就只有两种:\(a_i\) 等于 \(1\) 或 \(a_i\) 等于 \(0\)。
那么就可以将 \(id\) 那一位去掉。 设 \(f_{i,j}\) 表示进行 \(i\) 次加一操作,\(j\) 次减一后的期望代价,有转移:
这样时间复杂度是 \(\operatorname{O}(m^2\log m)\),可以通过本题,但预处理逆元可以做到 \(\operatorname{O}(m^2)\)。
Anticube
首先有个想法:对每个 \(a_i\) 分解质因数,然后用 \(map\) 找到与之冲突的数,取两者中间出现最多的数。
但这样是 \(\operatorname{O}(n\sqrt{a_i})\) 的,无法通过。(不过好像可以 BSGS?
考虑优化:首先可以先将 \(\sqrt[3]{a_i}\) 内的质因数先除掉,这样是可以实现的。
那么剩下的数就一定是一下几种形式:\(p^2\),\(pq\) 和 \(p\)。
首先考虑剩下的是 \(p^2\),那么直接开根就行。
如果剩下的是 \(pq\),那么想要与它凑成完全立方数就只能含有 \(p^2q^2\),但 \(p,q\) 都是大于 \(3000\) 的整数
,所以 \(pq>10^{10}\),直接将答案加一,不要放进 \(map\)。
剩下 \(p\) 同理。
那么这样我们就以 \(\operatorname{O}(n\sqrt[3]{n})\) 解决了这道题。
PS:为了防止类似 abs((__int128)x) 之类的悲剧发生,这边建议手写 \(\log V\) 的开根。(虽然这题没有涉及 __int128,但有精度问题的函数少用)。
Minimum Sum of Maximums P
首先考虑把这个奇怪的贡献拆一下,可以拆成:\(\sum_{i=1}^{n-1}{\frac{a_i+a_{i+1}+|a_i-a_{i+1}|}{2}}\)。
为了避免一些奇怪的边界,我们在序列左右插入一个极大值,最后减去即可。
考虑到 \(a_i+a_{i+1}\) 是固定的,所以我们要最小化 \(\sum_{i=1}^{n-1}{|a_i-a_{i+1}|}\)。
设区间左边那个固定点值为 \(x\),右边那个值为 \(y\),若 \(x\le y\),那么直接将这段数从小到大排序一定不会更劣。类似的,如果 \(x>y\),那么可以直接从大到小排,证明显然。
对于一段区间,设该区间的最大值为 \(M\),最小值为 \(m\),那么该区间的代价是:
(其中 \(L_i\) 和 \(R_i\) 是这段区间左右固定的数的值)
那么我们现在的任务就是分配每一段的最小值和最大值。
结论:对于两段编号为 \(i,j\) 的区间,这两段区间的值域要么完全包含,要么没有相交。
直接反证是比较简单的。
那么我们就可以设计状态,设 \(f_{l,r,s}\) 表示当前值域区间为 \([l,r]\),已经固定了的区间集合为 \(s\),那么有转移:
-
没有值域恰好为 \([l,r]\) 的区间:\(f_{l,r,s}=\min{f_{l+1,r,s},f_{l,r-1,s}}\)
-
将两段拼起来,设 \(len_i\) 集合为 \(i\) 的段的长度和,有:\(f_{l,r,s}=\min{f_{l,l+len_T-1,T}+f_{l+len_T,r,s-T}}\)。
-
正好有 \([l,r]\) 的区间:\(f_{l,r,s}=\min{f_{l+1,r-1,s-{x}}+Calc_x{l,r}}\),其中 \(Calc_x{l,r}\) 是第 \(x\) 段值域为 \([l,r]\) 时的代价。
这样时间复杂度是 \(\operatorname{O}(3^kn^2)\) 的,瓶颈在第二个转移。
[ABC305Ex] Shojin
首先考虑如果只能分一段,怎样排列是最优的:
首先如果 \(a_i=1\),那么肯定是将 \(i\) 放在最后面。
接着设现在有两个数 \(i\),\(j\),且 \(i\) 放在前面比 \(j\) 放在前面优,此时满足什么性质。
可以得到 \(i\) 放在前面的代是:\(a_j(a_i x+b_i)+b_j\),\(j\) 放在前面的代是:\(a_i(a_j x+b_j)+b_i\)。
那么此时有: $$a_j(a_i x+b_i)+b_j\le a_i(a_j x+b_j)+b_i$$
化简一下:$$a_ia_jx+a_jb_i+b_j\le a_ia_jx+a_ib_j+b_i$$
最终得到:$$\frac{b_i}{a_i-1}\le \frac{b_j}{a_j-1}$$
因为已经判掉了 \(a_i=1\),所以可以直接除。
接着考虑计算答案。设 \(f_{i,j}\) 表示前 \(i\) 个数分成了 \(j\) 段代价的最小值,有转移:
其中 \(F(k+1,i)\) 是区间 \(\left[k+1,i\right]\) 的代价。
这样复杂度是 \(\operatorname{O}(n^3)\) 的,无法通过。
考虑优化。首先先把 \(a_i=1\) 全部删掉,因为这些数的贡献都是 \(b_i\),最后加上即可。
这时可以发现,我们选择的每一段的长度最多是 \(\operatorname{O}(\log_2 X)\) 级别的。因为此时所有的 \(a_i>1\),且\(b_i\ge 1\),所以如果选择了长度为 \(x\) 的区间,那么这个区间的值至少是 \(2^{x-1}\)。
那么就可以处理所有长度小于等于 \(\log x\) 的区间的代价,每次转移时也只枚举最近的 \(\log x\) 个位置,暴力转移即可。
此时时间复杂度降到了 \(\operatorname{O}(n^2\log x)\)
那么现在优化方向只能放到第二维上了。
一种显然的方法是二分最少需要多少段,然后用 wqs 二分去限制段的数量,进行 dp。时间复杂度 \(\operatorname{O}(n\log^3 X)\),也许可以通过(?
但实际上我们并不需要二分最少需要多少段。直接用 wqs 二分。对于一个代价 \(c\),每增加一段代价增加 \(c\)。如果代价为 \(c\) 时最后的答案小于等于 \(X\),那么就往大的数找,否则往小的数找。要注意如果转移的值相同要优先选段数最大的(这样能让最终实际的代价最小),然后如果二分出的 \(x\) 对应的 \(f_n\) 比 \(m\) 小,就可以进行调整,将段数减少 \(1\) 并且让 \(f_n\) 增加 \(x\)。
最后特判一下所有 \(a_i\) 都等于 \(1\) 的情况。
[北大集训 2021] 简单数据结构
大大大大大ds。
首先考虑考虑最开始所有的 \(a_i\) 都为 \(0\) 时怎么做。
那么此时 \(a\) 是单调不降的,用线段树维护区间和区间最大值并支持对 \(a_i\) 加上 \(i\),每次 checkmin 就是把一段后缀赋值成某一个数,至于后缀位置就是从后往前最后一个大于操作给出的 \(x\) 的位置,在线段树上二分即可。
接着考虑如果 \(a_i\) 最开始不等于 \(0\) 怎么办。
不难发现,那些受到操作 \(1\) 影响的数一定是递增的,而没有受到操作 \(1\) 影响的数可以被表示成 \(a_i+k\times i\)(其中 \(k\) 是操作 \(2\) 的次数),所以考虑把这 \(n\) 个数分成受到操作 \(1\) 影响和没有受到操作 \(1\) 影响两个部分,设没有被影响的集合为 \(S\),被影响的集合为 \(T\),用线段树维护即可。
那么现在的问题是如何在 \(checkmin\) 的时候把 \(S\) 中的一些元素快速的丢到 \(T\) 里去。
考虑哪些要从集合 \(S\) 丢到 \(T\) 中的数满足什么性质。令当前操作 \(2\) 此时为 \(k\),不难发现这些数要满足 \(a_i+k\times i\geq c\),移项可以得到 \(a_i\geq c-k\times i\)。这相当于要将一个半平面内的数从 \(S\) 丢到 \(T\),如图:

这些红色的点就是需要移除的。
考虑分块凸包。把每个数看作一个点 \(\left(i,a_i\right)\),然后分成 \(\sqrt{n}\) 个块。每个块内建立一个下凸壳,每次暴力在凸壳上找到一个点使得 \(y\geq k\times x+b\)(\(k\) 在本题中是当前操作 \(2\) 的个数的相反数,\(b\) 是 checkmin 时题目给出的 \(x\)),然后把这个点删掉。删掉后可以直接暴力重构凸壳。这样子每次删掉一个点复杂度是 \(\operatorname{O}(\sqrt{n})\) 的(因为已经满足 \(x\) 递增了,不需要排序),一个点只会被删 \(1\) 次,所以这部分的时间复杂度是 \(\operatorname{O}(n\sqrt{n})\) 的。删除的时候可以双指针做(每次删点不要重新从凸壳的第一个点开始遍历,直接从被删除的点的前一个开始),但其实直接从头遍历好像复杂度也是对的(?
由于 \(T\) 中不一定包含所有数,所以线段树要记录每个位置上的数有没有加入 \(T\),记录的信息只记录已经加入了 \(T\) 的那些数。集合 \(S\) 可以用树状数组维护,因为只要维护第 \(i\) 个位置是否存在 \(S\) 中,然后每次找到在分块凸包中被删除的数单点修改。
总时间复杂度 \(\operatorname{O}(n\sqrt n)\)
代码比较复杂,细节比较多,要稍微注意一下线段树下传 tag 时加法和赋值的顺序以及对区间最大值和区间和的影响。
P5064 [Ynoi2014] 等这场战争结束之后
个人感觉比较友好(?的一道 Ynoi。
首先操作 2 非常难受,所以直接根据操作建一棵树,那么就可以通过遍历这个树来完成回退操作了。
维护连通块可以使用并查集,而查询区间第若干大可以用分块。分块记录某个块内以某个点为根有多少点,合并两个联通块时直接暴力把分块的每个块加上,撤回时同理。
这样空间是 \(\operatorname{O}(n\sqrt{n})\) 的,无法通过,考虑优化。
由于该算法常数较小,所以时间上有很大的余地,所以可以调大块长始块数变少。实测 \(d=2\times 10^3\) 最好。
此时只有 24 分,但是记录块内信息的数组的值不会很大,所以可以直接用 short。
这样有 88 分,最后用 inline 优化一下,然后选择一个较好的语言即可通过。
最后注意一下并查集因为要撤回所以不能路径压缩,要启发式合并。
DZY Loves Math

进行一个式子的推:
令 \(s=d'\times d\)。
\(\mu\) 函数可以通过线性筛进行预处理,考虑怎么处理 \(f\) 函数。考虑线性筛在筛质数时筛的方式是每次找到最小质因子,所以记录当前的最小质因子和最小质因子的次数和 \(f\) 的值,转移时更新即可。然后枚举每个 \(\mu\) 不为 0 的数,更新其每个倍数的值。
最后对 \(s\) 进行整除分块,时间复杂度 \(\operatorname{O}(n\log n+T \sqrt{n})\),不过预处理只要考虑 \(\mu\) 不为 0 的数,所以跑不满。
[AGC064C] Erase and Divide Game
首先考虑数的数量很少可以一个个加入该怎么处理。考虑删除一个所有的奇数或偶数并且将剩下数除以 2 会产生什么影响。考虑将一个数从最低位向最高位插入 trie 树中。那么每一次操作对应的影响就是向左儿子/右儿子走一步。所以将每个数都插到 trie 中,那么一个人输了就是到达了一个空子树。在 trie 上做 dp 即可。
考虑区间长度很长的情况下该怎么优化。考虑将区间 \([l,r]\) 分解成若干个 \([x,x+2^d)\) 的区间,那么每个区间内的所有数插入到 trie 中对应的就是一个大小为 \(2^d\) 的完美二叉树,下面接着一条链。而链的数量暴力插入是可以接受的,考虑优化掉完美二叉树的插入。
考虑建 \(60\) 棵 trie,第 \(i\) 棵 trie 表示这个完美二叉树的大小 \(2^i\)。那么长度为 \(2^i\) 的区间就插入第 \(i\) 棵 trie 中。
考虑在这 \(60\) 棵 trie 上进行 dp。设 \(f_v\) 表示在这 \(60\) 个 trie 上第 \(i\) 个 trie 上当前这个位置为 \(v_i\)。转移时将每个点向左儿子或右儿子移动即可。
实现时对于第 \(i\) 棵 trie 先构建一条长度为 \(i\) 的链。
时间复杂度按照实现方式为 \(\operatorname{O}(n\log^2 n)\) 到 \(\operatorname{O}(n\log^3 n)\)。
[AGC063C] Add Mod Operations
首先考虑如何判断无解。对于一对 \(i,j\),如果 \(a_i=a_j\) 且 \(b_i\neq b_j\) 那么就一定无解,否则一定有解。
对于 \(a,b\) 都相同的一对数,可以只考虑一个位置,所以可以对原数组去重。然后按照 \(a\) 排序。
考虑选择了一对 \(x,y=x+\max a\) 进行操作会对序列产生什么影响。将所有数放在数轴上,如图(借用了 at 的官方题解的图):

相当于是把从 \(a_n\) 到 \(y\) 这条线段移动到了整体的前面。
通过若干次平移可以得到如下形式:

如上图,有 \(a_2=0,a_i=a_{i-1}+x_{n-i+2},a_1=a_n+x_1\),所以现在的目标是构造出 \(x\),使得所有的 \(a_i=b_i\)。
首先有 \(a_2\) 是等于 \(0\) 的,这显然不符合要求,所以最后可以进行一次 \(x=b_2\),\(y=2\times 10^9\) 的操作,那么现在要让所有的 \(a_i=b_i-b_2\)。
考虑到 \(a_i=a_{i-1}+x_{n-i+2}=b_i-b_{i-1}\),所以 \(x_{n-i+2}=b_i-b_{i-1}\)。然后考虑第一次操作要减去 \([0,a_1]\) 这一段,所以 \(X=b_1-b_m-a_1\)。
[AGC064D] Red and Blue Chips
神仙找性质数数。感觉题解有些地方没太写清楚,这里稍微补充一些。
考虑校验答案序列的合法性。首先 R 和 B 的数量显然要和原串一致,接着考虑反向进行操作,在原串中从后往前遍历,维护当前字符串集合。
从第 \(n\) 位开始,如果第 \(s_i=\) R,那么直接将当前的所有字符串中找到任意一个开头为 R 的字符串将开头的字符移除(如果找不到 R 开头的串就说明该答案串不合法)。否则如果 \(s_i=\) B,那么可以将当前的字符串集合中在原串中的任意一个串分成两个以 B 结尾的分裂前的串的前缀和后缀,塞入当前的字符串集合中。
考虑模拟对原串为
RBBRRB,答案串为RRBRBB进行如上操作。\(i=6\) 时,原串分裂成
RRBRB和B,由于原串以B为后缀,删除B。\(i=5\) 时,\(s_i=\)
R,将RRBRB开头的R删去,此时字符串为RBRB。\(i=4\) 时,\(s_i=\)
R,将一个RBRB最开头的R删去,答案串变为BRB\(i=3\) 时,\(s_i=\)
B,对字符串分裂成B和RB,由于原串以RB为后缀,所以删除RB中的B,变为R。\(i=2\) 时,\(s_i=\)
B,删除B,此时还剩下RB。\(i=1\) 时,\(s_i=\)
R,删除字符串R此时,集合为空,说明该答案串合法。
分析一下我们操作的过程,每次遇到 R 的操作显然删除任意一个开头的 R 都是一样的,但遇到 B 的时候分裂的方式会影响最终的结果。由于可以任意进行分裂,而不合法情况是由开头的 R 决定的,所以分裂时会选择可以分裂出的 R 最多的方案。
令 \(a_i\) 表示答案串中第 \(i\) 个极长 R 连续段的长度,\(k\) 表示答案串中有多少极长R 连续段,\(b_i\) 表示将原串 \(\operatorname{reverse}\) 后第 \(i\) 个极长 R 连续段的长度,\(v\) 表示原串中的极长 R 连续段个数。于是在同满足如下条件时合法:
-
\(\sum_{i=1}^{k} a_i=\sum_{i=1}^{v} b_i\)
-
\(a_1\ge b_1\)
-
将 \(a_{2},a_3,a_4 \cdots a_k\) 从大到小排序,有:\(\forall x,\sum_{i=1}^{x}a_i\ge \sum_{i=1}^{x} b_i\)。
这个式子当时想了很久才想明白,所以稍微解释一下。
要从大到小排序是因为每次分裂时可以选择让 R 的数量尽量的多,但是由于开头的一段最开始就可以取出,所以 \(a_1\) 不参与排序。\(\forall x,\sum_{i=1}^{x}a_i\ge \sum_{i=1}^{x} b_i\) 是因为每次分裂后开头的 R 数量只有 \(\sum_{i=1}^{x}a_i\) 个,而此时在操作中已经删除了 \(\sum_{i=1}^{x}b_i\) 个 R,所以如果答案序列要合法必须要满足当前所有分裂出来的字符串开头的 R 的数量足够进行操作。
那么现在问题就转化为了对 \(a\) 数组计数(下文中的 \(a\) 数组如果没有特殊说明指的都是去掉 \(a_1\) 后的数组)。
首先对排序后的 \(a\) 数组计数是简单的。但是因为可能会有相同的值,所以不能直接乘上 \((k-1)!\)。假设去掉 \(a_1\) 后有 \(m\) 种数,第 \(i\) 种数有 \(c_i\) 个,那么这个排序后序列所对应的方案数为:
由于 \((k-1)!\) 是固定,所以考虑对系数进行计算。
设 \(f_{i,j,k}\) 表示当前最小值为 \(i\),已经确定了 \(j\) 个数,所有数的和为 \(k\),所有满足该条件的系数之和。
拓扑序为 \(i\) 从大到小(因为 \(a\) 要从大到小排序)。
假设原串中 R 的个数为 \(cnt\),有转移:
其中在从小到大枚举 \(l\) 的过程中如果出现 \(l\times i+k<a_{l+j}\) 就要退出循环不再进行转移(具体实现可以参考代码)。
这样做看起来时间复杂度是 \(\operatorname{O}(n^4)\) 的,但是 \(j,k\) 只有 \(n^2\) 对,而 \(i,l\) 的对数大概为 \(\sum_{i=1}^{n}\frac{n}{i}\),这是调和级数,所以只有 \(n\ln n\) 对。
因此总时间复杂度为 \(\operatorname{O}(n^3 \ln n)\)。
[AGC062B] Split and Insert
考虑倒序进行处理,即把题目给出的的排列 \(P\) 变为 \(1,2,3,\cdots\cdots,n\),那么一次操作就成了从当前序列中取出 \(k\) 个数插入到序列的最后面。
考虑到数据范围很小,所以大概率可以 dp。设 \(f_{i,l,r}\) 表示操作了 \(i\) 次,\(\left[l,r\right]\) 这段值域对应的位置已经排好序。有两种转移:
- 当前时刻不进行操作,那么直接从前一时刻继承,有:
- 当前时刻会进行操作。由于只能选出一个子序列插到最后,所以当前这段排好序的区间只能从两个已经排好序的区间通过操作合并,有:
初始时对于每一对 \(l,r\),满足 \(l\) 到 \(r\) 的数的位置有序,有 \(f_{0,l,r}=0\)。
总时间复杂度为 \(\operatorname{O}(n^4)\)。
Beautiful Bracket Sequence (hard version)
一个序列的深度是子序列的最大值,所以考虑如何去确定这个最大值。
假设没有 ?,可以设 \(f_i=\sum_{j=1}^{s_j=`(`}-\sum_{j=1}^{s_j=`)`}\),不难发现 \(f_0<0\),\(f_n>0\),而 \(i\) 从 \(0\) 到 \(n\) 一定是在一直加一,所以找到 \(f_i=0\) 的 \(i\) 就是最佳决策点。所以如果序列中一共有 \(y\) 个右括号,那么 一定有 \(f_y=0\)。所以只需要统计 \([y+1,n]\) 这个序列中的右括号个数即可得到该序列的深度。
所以我们可以在右括号和左括号之间统计贡献。对于 \(s_i=\) ?,其只有在选择右括号时会产生贡献,并且所有右括号的数量之和要小于当前位置。如果一共有 \(x\) 个问号,那么方案数为 \(\sum_{j=0}^{i-y-2}\tbinom{x-1}{j}\)。同理,如果一个位置为右括号,则贡献为 \(\sum_{j=0}^{i-y-1}\tbinom{x}{j}\),这两个东西可以前缀和处理,总时间复杂度 \(\operatorname{O}(n)\)。
Move by Prime
首先考虑对于一个序列怎么计算。考虑到每次操作只能乘上或初除以一个质数,而最后如果数相等那么每个质数的出现次数一定相等。所以对于每个质数分开考虑,显然需要将每种质数的个数变成所有的该质数的出现次数的中位数。
那么对于一个序列,令质数 \(p\) 的出现次数的中位数为 \(v_p\),代价为:
由于 \(cnt\) 数组是固定的,所以考虑每个数在系数为 \(-1\) 和系数为 \(1\) 时分别会作为子序列中的一个数贡献多少次,即可计算出结果。
于是考虑贡献该如何计算,对于当前计算的一个定值 \(p\),\(p\) 作为因子在这 \(n\) 个数中的出现次数分别为 \(c_1,c_2,c_3\cdots\cdots c_n\)。对 \(c\) 从小到大排序那么第 \(i\) 个位置在所有子序列中系数的贡献就是:
其中 \(j\) 是质因数 \(p\) 出现次数比 \(c_i\) 大的数选了的个数加上 \(p\) 的出现次数比左边小的数的数中没有出现的数。
最终枚举 \(p\),对所有 \(c\) 排序,暴力算即可。不过有一些细节,比如说 \(c_i=0\) 的数要特殊考虑一下。
总时间复杂度 \(\operatorname{O}(n\log^2n)\),实际上根本跑不满。
Weak Subsequence
一道很不错的数数题。
结论一:如果存在一个长度为 \(x\) 的弱字串,那么一定存在一个长度为 \(x\) 的弱字串使得这个弱字串是由一个字符和一个长度为 \(x-1\) 的字串组成的。
考虑为什么是对的,观察下面一张图:(其中黑色是原串,红色是选出来的子序列,蓝色是子串)

那么如下这种方案显然是一样的:

所以该结论显然是正确的。
结论二:极长的弱子串的子序列和子串一定开头在整个串的开头要么结尾是整个串的结尾。
这个结论比较显然,如图:

那么往左延伸时显然子串和子序列每个位置的字符都是相同的,所以一定可以延伸到两端。
对于前缀的一段,那么最长弱子串就是最后面的从后往前第一个相同的两个字符中靠前的那个字符的位置加上一。后缀同理。
跟据鸽巢原理,最多在第 \(k+1\) 一个位置就会出现两个相同的字符。于是暴力枚举不同的位数,组合计算即可。
至于如何计算,可以容斥。用最长弱子串长度 \(\le w\) 的串的数量减去长度 \(\le w-1\) 的串的数量。
剩下的直接分类讨论前面第一次相等的位置的前缀与后面第一次相等的位置的后缀是否重叠,直接计算即可。
ZerOne

考虑每次操作是交换两个子串,不难发现,每次交换完所有 1 所在的位置的编号和是不变的。而所有 1 所在位置的编号和与原串相等的串可以证明一定是可以构造出来的。所以本题等价于计数位置的编号和与原串相等且 1 的数量相等的串的个数。
考虑 dp。设 \(f_{i,j,k}\) 表示前 \(i\) 个位置,填了 \(j\) 个 1,位置编号和为 \(k\) 的序列的个数。转移时分类讨论这一位填不填即可。
总时间复杂度 \(\operatorname{O}(n^4)\)。
[JOI Open 2016] 销售基因链
好题!可惜做的时候一直在想 hash 做法还没想出来......
首先考虑没有后缀的情况。那么可以直接放在 trie 上查子树内字符串出现个数,dfs 预处理一遍就好。
考虑现在加入后缀。那么我们可以对后缀也建立一棵 trie,那么问题转化为同时出现在两棵 trie 的两个子树内的字符串个数。
考虑求出 trie 的 dfn 序,那么现在就相当于转化成了同时在两个区间内出现的字符串个数。设一个字符串 \(s\) 在第一个 trie 上出现的位置是 \(i\),在第二个 trie 上出现的位置是 \(j\)。那么我们可以将其设为点 \((i,j)\)。那么询问就可以转化为一个矩阵内点的数量,离线二维数点即可。
(ox) Alternative

一道很好的格路计数题。
首先我们可以注意到 o 的存在其实没有很大的意义,只要在最后将答案乘上 \(\binom{A+B+C+D}{C}\) 即可。
接着考虑先找一些性质将问题简化一下。不难发现当 \(A\neq B\) 时,如果 \(D\neq 0\),答案一定为 \(0\)。\(A=B=D=0\) 且 \(C\neq 0\) 时答案为 \(1\),那么接下来只要考虑 \(A=B\) 的情况,于是现在把右括号的数量也视为 \(A\)。
考虑如果已经确定了 \(A\) 个左括号和 \(B\) 个右括号的排列情况,要将若干个 x 插入到序列中的方案数如何计数。注意到 x 插入的位置其前缀的左括号一定比右括号多至少一个,后缀的右括号一定比左括号多至少一个,那么以下这些黑线在的位置就是不合法的:

注意到这些黑线将整个括号序列分割成了若干个合法的括号匹配,假设一共分成了 \(k\) 个括号段,那么插入的方案数就是 \(\binom{2A-k}{D}\)。
那么就可以枚举一共分成了多少个括号段,然后计算方案。假设当前分成了 \(i\) 个括号段,考虑该如何计算方案数。
首先先将这个问题稍微变下形,如图:

不难证明,这三种形式是等价的。
将一个左括号视为在一个大小为 \(A\times A\) 的矩阵上向右走一步,右括号为向上走一步。那么原问题就可以变为钦定了先向右走 \(i\) 步,然后走到点 \((A,A)\) 且不越过直线 \(y=x\) 的方案数,如图:

答案就是从 \((i,0)\) 走到 \((A,A)\) 的方案数。
考虑与从 \((0,0)\) 开始走一样操作。对于一条越过了 \(y=x\) 这条直线的路径,可以在其第二次到达直线 \(y=x\) 时将其剩下的部分以 \(y=x\) 这条直线为中心翻折,比如下图绿色路径翻折成了紫色的路径:

一条合法的路径最后一定会经过点 \((A, A-1)\),所以说一条不合法的路径一定会经过点 \((A-1, A)\),所以说用 \((i,0)\) 到 \((A,A)\) 的方案数减去 \((i,0)\) 到 \((A-1,A)\) 的路径数即可,所以方案数如下:
那么答案式子为:
对该式子进行化简:
考虑将枚举 \(i\) 换为枚举 \(A-i\)
[AGC038E] Gachapon
一道很好的题,感觉和 重返现世 有点像,但推式子更为困难。
首先看到每个题目要求每个 \(i\) 都出现了至少 \(B_i\) 次,不难发现这是要求对于每个位置 \(i\) 出现了 \(B_i\) 次的期望随机次数的最大值,可以考虑使用 min-max 容斥:
那么问题就变为了对于一个集合 \(T\),期望操作多少次使得其中某个数出现次数大于等于 \(B_i\)。
首先如果一次操作选择的数在集合 \(T\) 之外那么这次操作是没有意义的。设 \(s=\sum_{i=1}^{n} a_i\) (下文的 \(s\) 都是这个含义),不难计算出选择的数在集合内的期望操作次数:
那么现在只需考虑每次操作都选入了集合内的情况,最后乘上 \(P\) 即可(若有 \(k\) 次选在了集合里那么期望就是 \(k\times P\))。
考虑需要多少次选择才能满足条件。可以通过如下式子进行转化:
于是现在只要考虑每次操作完都不满足的概率之和即可,也就是说要计算操作出一个数组 \(c\) 满足 \(\forall i\in[1,n],c_i<B_i\) 且 \(\sum\limits_{i=1}^{n}c_i=k\) 的概率。这个东西很好计算,公式如下:
设 \(F(T)=\sum\limits_{i\in T}a_i\),把原式变换一下形式得到:
现在已经计算出了如果每次都选入集合的期望次数,乘上 \(P\) 可得到 \(T\) 内存在位置满足其出现了至少 \(B_i\) 次的期望操作次数(其中 \(G(T)=\sum_{i\in T}(B_i-1)\)):
那么最后答案的式子就是:
考虑我们需要得到的信息:
- 容斥系数
这个东西很好处理,在后续 dp 转移时每次选择一个数就取反符号即可(详见 dp 转移式)。
- \(F(T)=\sum_{i\in T}a_i\)
也就是说我们需要记录集合中出现的数的 \(a\) 之和。
- \(\sum\limits_{i=1}^{n}c_i=k\)
也就是说要记录选择了的序列的和。
剩余的贡献可以在 dp 中计算。
于是可以开始 dp 了。设 \(f_{i,j,k}\) 表示前 \(i\) 个数,我选择的这些数所选择的 \(c\) 的和为 \(j\),我选择的数的 \(a\) 的和为 \(k\),有转移方程:
注意一下边界条件 \(f_{0,0,0}=-1\) 即可。
[AGC036D] Negative Cycle
神仙题。
发现这个东西在图上非常不好处理,考虑转化一下。
注意到题目的限制是不存在负环,于是很自然的能想到 SPFA。对于一张图,若其没有负环,则说明其差分约束有解,于是转化成了一个序列问题。
假设我们现在确定了最优的删边策略下差分约束的解为 \(a_1,a_2,\cdots,a_n\)。由于初始的 \((i,i+1,0)\) 的边无法去掉,所以一定有 \(\forall i\in [2,n]\ a_i\geq a_{i-1}\),考虑其差分数组 \(b\),那么有 \(\forall i\in [1,n]\ b_i\geq 0\)。
那么对于一对数 \((i,j)\) 满足 \(i<j\),那么考虑其两条边能选择的情况的性质。
- 边 \((i,j,-1)\) 可选择
那么有 \(a_i-a_j\geq 1\),即 \(\sum\limits_{k=i}^{j-1}b_k=0 \geq 1\)。
也就是说如果 \(\sum\limits_{k=i}^{j-1}b_k=0\) 就需要花费代价删除这条边。
- 边 \((j,i,1)\) 可选择
那么有 \(a_i-a_j\leq 1\),即 \(\sum\limits_{k=i}^{j-1}\leq 1\)。
也就是说如果 \(\sum\limits_{k=i}^{j-1}b_k\geq 2\) 就需要花费代价删除这条边。
观察需要删去每条边的限制,不难发现对于差分数组的每一个 \(b_i\),一定有 \(b_i\le 1\),因为这样相较于 \(b_i\geq 2\) 可以多满足 \((i+1,i)\) 这个限制且不会影响别的限制,于是现在就是要确定一个长度为 \(n\) 的 01 序列 \(b\)。
设 \(f_{i,j}\) 表示 \(b_i=1\),且上一个为 \(1\) 的位置为 \(j\),于是有转移:
其中 \(Val(i,j,k)\) 是这一段产生的代价,分类讨论跨过的 \(1\) 个数不同的区间,有二维前缀和维护一下即可。
ソリティア
先分析一下怎样的矩阵可以填满。
首先四个角的位置显然不可能满足两条条件中的任何一条,所以这四个角必须已经填满。
对于第一行和第三行的位置,其不能满足第一种条件,所以只能通过条件二填满。那么就不能存在一个长度大于等于 \(2\) 的连续段都是空的。
接下来考虑计数。可以考虑对每个位置分配一个数,代表其是第几个被填满的,类似于 AT_dp_t。
设 \(f_{i,j,0/1}\) 表示第 \(i\) 列,中间位置是第 \(j\) 个被填的,填的方式是上下都满了/左右都满了。
转移时分类讨论一下当前位置的填法与上一个位置的填发即可。
- 当前位置通过操作 1 填。
前一列的填法可以是任意一种,且编号不重要,那么在这一列的第一行和第三行中对于没有填的位置选择一个比当前位置的编号小的即可。
- 当前位置通过操作 2 填
那么前一列一定要先填满,所以前一列的编号要小于这一列且填法一定是第一种,而这一列的第一行和第三行的编号大小关系不重要,乘上方案数即可。
两种情况都是 \(\operatorname{O}(n)\) 的,但是可以用前缀和优化。总时间复杂度 \(\operatorname{O}(n^2)\)。
[ABC323G] Inversion of Tree
一道线性代数大杂烩。
前置知识:高斯消元,特征多项式,矩阵树定理,以及一些线性代数的理论知识。
对于这种无根树的数量计数问题,不难想到可以用矩阵树定理。但问题在于这里有逆序边的数量限制。
考虑类似 [省选联考 2020 A 卷] 作业题 的做法,把矩阵中的元素变成多项式从而计算答案。对于一条顺序边,其在矩阵上的边权为 \(1\),逆序边边权为 \(x\)。设构造出的矩阵为 \(A\),那么有 \(k\) 条逆序边的树的数量就是 \([x^k] |A|\)。但这道题不像 作业题 一样要对 \(x^2\) 取模,所以复杂度是 \(\operatorname{O}(n^4)\) 的,无法通过。
考虑转换一下思想,设顺序边组成矩阵为 \(A\),逆序边组成的矩阵为 \(B\),那么 \(k\) 条逆序边的无根树的方案数就是 \([x^k]|A+xB|\)(实际上这一句是废话),考虑有没有什么办法快速计算这个东西。
设 \(I\) 为单位矩阵,考虑一下两种特殊情况:
1. \(B=I\)
那么 \(|A+xB|=|A+xI|\),不难发现这个式子就是特征多项式(\(|xI-A|\)),于是此时答案就是 \(-A\) 的特征多项式。
2. 存在 \(B'\) 满足 \(B'B=BB'=I\)(即 \(B\) 有逆元)
那么 \(|A+xB|=\frac{|AB'+xBB'|}{|B'|}=\frac{|xI-(-AB')|}{|B'|}\),直接矩阵求逆一下就行了。
通过前面两个例子的启发,我们可以发现只要把 \(B\) 转化为 \(I\) 那么就是好做的。但问题在于 \(B\) 不一定有逆元。
我们不妨回忆矩阵求逆的过程,之所以 \(B\) 没有逆元是因为某一行没有主元。而在这道题中,我们除了 \(B\) 外还有一个矩阵 \(A\),所以不妨通过 \(A\) 来让 \(B\) 拥有主元。
首先,在高斯消元时,如果 \(B\) 的第 \(i\) 行没有主元时可以直接把第 \(i\) 列 全部变成 \(0\)。不难发现,这时候我们将 \(A\) 的第 \(i\) 列乘上 \(x\) 就相当于交换 \(A\),\(B\) 的第 \(i\) 列,这样就可以让 \(B\) 的第 \(i\) 行存在主元了。
当然,可能会出现操作后 \(B\) 的第 \(i\) 行也没有主元的情况,考虑这时候的状况。这说明 \(A\),\(B\) 的第 \(i\) 列都为空,那么 \(|A+xB|\) 必然为 \(0\), 所以此时特征多项式就是 \(0\),可以特判掉。
通过这个操作,我们可以保证 \(B\) 能够变成 \(I\),那么套一个特征多项式板子就好了。
时间复杂度 \(\operatorname{O}(n^3)\)。
[AGC043C] Giant Graph
一道很有教育意义的题。
题目描述
感觉题目讲的很清楚了,建议读原题题面。
题解
首先观察一下题目的一些特殊性。注意到对于点 \(\left(x,y,z\right)\),其代价是 \(\left(10^{18}\right)^{x+y+z}\) 这件事情非常鬼畜,不妨从这一点入手。
类似很多 01-trie 上贪心的题目进行考虑,由于 \(10^{18}\) 远大于 \(n\),所以不难发现我们肯定要尽量选择 \(x+y+z\) 大的点。于是就有了一个 \(\operatorname{O}(n^3)\) 的贪心,把每一个点都建出来,然后按照 \(x+y+z\) 从大到小选点,能选就选。
根据上面这个贪心,我们可以把无向边 \(\left(u,v\right)\) 变为一条有向边,从编号小的点连向编号大的点。这样整张图就变成了 DAG。
继续分析 最大权独立集 这个东西。令 \(f_{x,y,z}\) 表示点 \(\left(x,y,z\right)\) 有没有选,我们规定 \(f_{x,y,z}=0\) 表示选,其他情况都表示不选。设 \(G\) 表示这些 三维 的点通过这三张图连出的图,由于这是一个 DAG ,所以可以反拓扑序 dp,转移如下:
如果出边连向的点是已经选择(即 \(f\) 值为 \(0\))的,那么当前点的 \(\operatorname{mex}\) 值一定不会为 \(0\),这是显然的。所以这样转移可以保证不会选择相邻的,而且我们是从权值大的点开始 dp 的,所以保证了权值最大。
观察一下,注意到 \(f\) 的 dp 与 SG 函数的计算方式很像(这也是这道题最有教育意义的地方),实际上这两个可以说一模一样。那么便考虑能否像 SG 函数一样把 若干维分开 进行 dp,最后异或在一起。
考虑 SG 函数在什么时候可以分开计算最后用异或合并,我们只要保证每一维都互不干扰。而这道题中三张图上的边都只会改变某一维的编号,所以说并不会互相干扰。于是我们可以直接拆开考虑。
设这三张图上点 \(x\) 的 SG 值分别是 \(F(x),G(x),H(x)\),那么答案就是:
但这样依旧是 \(\operatorname{O}(n^3)\) 的,考虑优化.
设 \(F'(x)\) 表示在第一张图上 SG 值等于 \(x\) 的点的权值之和,\(G'(x)\),\(H'(x)\) 同理。那么答案式子变为:
直接暴力是 \(\operatorname{O}(n^2)\) 的,但 SG 函数的值最大是 \(\operatorname{O}(\sqrt m)\) 级别的。感性理解一下,对于一个 SG 函数值为 \(x\) 的点,其至少需要向 \(\left[0,x-1\right]\) 中每个权值的任意一个点连边,归纳的考虑,我们想要有一个 SG 值为 \(x\) 的点至少需要 \(\frac{x\times \left(x+1\right)}{2}\) 条边,所以 SG 值是 \(\operatorname{O}(\sqrt{m})\) 级别的。
这样我们就可以直接枚举前面答案式中的 \(x,y\) 暴力计算了,时间复杂度 \(\operatorname{O}(m)\)。
扩展
题目描述
在原题目的基础上,点由三维变成了 \(k\) 维,但每一维的图都是一样的(图会给定)。对于城市 \(\left(p_1,p_2,\cdots,p_k\right)\),其代价为 \(10^{n^k\left(p_1+p_2+\cdots+p_k\right)}\),依旧求最大权独立集。
\(1\le n\le 2\times 10^5,1\le k\le 10^{50000}, 1\le m\le 5\times 10^5\)
题解
首先这个权值显然是搞笑的,这道题怎么处理这个东西就怎么处理。至于权值计算可以用十进制倍增计算 \(n^k\) 然后费马小定理解决。
与这题一样,我们将其转化为 SG 函数的问题,由于这道题每一个维度图都一样,所以每一维的 SG 函数是一样的。
定义多项式运算:
设 SG 值为 \(x\) 的点权值和 为 \(f_x\), \(F(x)=\sum_{i=0} f_i\times x^i\),那么答案就是:
那么直接做的时间复杂度就是 \(\operatorname{O}(m\log k)\),这个式子显然可以用 \(FWT\) 优化,可以优化到 \(\operatorname{O}(\sqrt{m}\log \sqrt{m}\log k)\)。
PS:这里有一个小 trick,可以直接对 \(k\) 进行十进制倍增,就不用写高精度了。
[ARC168E] Subsegments with Large Sums
题目描述
给定一个长度为 \(n\) 的序列 \(a\),将 \(a\) 划分为 \(k\) 个连续段,最大化满足连续段中元素和大于等于 \(s\) 的连续段数。
题解
非常好的一道题。
首先可以秒出个 \(\operatorname{O}(n^2k)\) 的 dp。设 \(f_{i,j}\) 表示前 \(i\) 个位置分成了 \(j\) 段,和大于等于 \(s\) 的方案数,有转移:
注意到一定是一个前缀作为转移点时 \(s\) 会小于等于 \(\sum_{l=k+1}^{i}a_l\),用单调队列可以优化到 \(\operatorname{O}(nk)\)。
发现我们要求的是 正好分成 \(k\) 段 的代价,这就很 wqs 二分,于是直接对第二维 wqs 二分就做完了........吗?
实际上这是一个很容易犯的错误,这相当于看到 正好 就直接认为这道题的做法是 wqs 二分 虽然这题确实是 。但实际上出题人不一定会这么好心。wqs 二分
我们考虑什么时候可以 wqs 二分,我们必须要保证 不同段数的代价组成的图形是凸的,而这道题中我们上面的 \(f\) 显然不是凸的,所以不能 wqs 二分。
继续优化上面的 dp 很难有所进展,所以考虑先分析一下性质。
既然这个 正好为 k 的限制不能被我们作为突破点,那不如将其转化一下。
我们令和大于等于 \(s\) 的一段是 好段,和小于 \(s\) 的一段是 坏段。不难想到,对于一个 坏段,在不考虑正好为 \(k\) 段的情况下我们肯定想要让其长度为 \(1\)。但这样子段数就有可能会大于 \(k\)。可是仔细思考一下会发现对于一个长度为 \(1\) 的 坏段,如果我们将其与左或右的段合并,会让我们的段数减少 \(1\),而且答案不会减少!于是我们可以将原题转化成如下形式:
给定一个数列 \(a\),将这个数列划分成 至少 \(k\) 段,设这 \(k\) 段中和大于 \(s\) 的段有 \(x\) 个,在 \(x\le k\) 的情况下求 \(x_{\max}\)。
这时看上去我们的题目好像变化不大,但实际上只需要稍微再分析一下就可以得到一个比较好做的子问题。
我们设 \(F(x)\) 表示这个数列中有 \(x\) 个段的和大于等于 \(s\) 时,这 \(x\) 个段的 长度 的最小值。那么对于一个 \(x\),如果在保留这 \(x\) 的段的情况下能分成至少 \(k\) 个段,必定有如下式子:
之所以要加上 \(n-F(x)\) 是因为剩下的那些段我们认为其是坏段,那么一定是每一段的长度都为 \(1\) 最优。
这时候一般可以猜测答案具有单调性。但不难发现 \(F(x)\) 是单调递增的,所以说 \(x-F(x)\) 不一定有单调性。这里卡了我很久,但其实只要重新考虑一下 \(F(x)\) 的定义就会发现其实这个地方的处理很简单。因为每一段的长度都要大于等于 \(1\),所以说如果 段数 减小 \(1\) 那么 所有段的长度和 至少减少 \(1\)。也就是有:
所以说直接二分 \(x\) 即可。
现在的问题就是如何快速求解 \(F(x)\)。也就是快速求出 正好 有 \(x\) 个段的和大于等于 \(s\) 的情况下这 \(x\) 个段的长度和的最小值。
先暴力 dp。设 \(f_{i,j}\) 表示前 \(i\) 个数,有 \(j\) 个段的和大于等于 \(s\),这 \(j\) 个段的长度和的最小值。
转移很简单,令 \(v_i=\sum_{j=1}^ia_j\),有转移如下:
优化思路与刚开始的错误 dp 一样,由于 \(v_i\) 递增,所以可以直接单调队列。
现在是一个很熟悉的场景。同样是 正好,类似的转移,那么这一次我们可以使用 wqs 二分吗?
答案是可以。
我们现在需要考虑的是 \(F(x)\) 是否有凸性。
证明一个函数是否有凸性有一下几种方式:
-
打表
-
建立费用流模型
-
归纳法
-
证明 \(F(x+1)+F(x-1)\geq 2F(x)\) 或 \(F(x+1)+F(x-1)\leq 2F(x)\)
在这里我们选择第四种方法,下面给出一个比较感性的证明。
证明:假设 \(x+1\) 的最优策略与 \(x-1\) 的最优策略方案如下:
考虑这两个方案存在什么性质,首先肯定不会存在 \(x+1\) 的某个区间包含了 \(x-1\) 的某个区间。
所以对于这两种方案所构成的极长连续区间,上下的区间个数差的 绝对值 不超过 \(1\),下面是上面那张图的差值:
需要注意这里没有画差值为 \(0\) 或 \(-1\) 的情况,但实际上是有的。
不难发现对于一个差值为 \(1\) 的段上下同时交换方案就会得到两个 \(x\) 的方案,但这个方案不一定是最优的。
于是我们证明了 \(F(x-1)+F(x+1)\geq 2F(x)\)。
既然已经知道了 \(F(x)\) 是凸的,那直接 wqs 二分优化就行了。时间复杂度 \(\operatorname{O}(n\log^2n)\)。
还可以再快一点吗?答案是可以的。由于内外的二分都有单调性,所以只需要二分斜率就可以了。时间复杂度 \(\operatorname{O}(n\log n)\)。
最后记得思考一下 wqs 二分时遇到三点共线的情况就可以了。
笔者很懒所以只写了 \(\operatorname{O}(n\log^2 n)\) 的,code。
[ABC311Ex] Many Illumination Plans
神仙题,感觉有点偏向论文,但还是比较有教育意义(?
题目描述
给定一棵 \(n\) 个点的树,每个点有权值 \(B_i\),重量 \(W_i\),颜色 \(C_i\)。
你可以进行若干次如下操作:
-
选择一个 非根 节点 \(u\)。
-
删去 \(u\),将 \(u\) 的所有子节点连向 \(u\) 的父亲。
定义一个树 \((V,E)\) 是好的,当且仅当:
-
\(\sum_{i\in V}W_i\le X\)
-
\(\forall_{\left(u,v\right)\in E},C_u\neq C_v\)
定义树的权值为 \(\sum_{i\in V}B_i\),对于给出的树的每个子树,求其能通过上述操作得到的好树的最大权值。
题解
考虑 dp。
不难设计出如下 dp:设 \(f_{u,i,0/1}\) 表示以 \(u\) 为根的子树中,选出了的点的重量和为 \(i\),选择的点中 离 \(u\) 最近的一层点 的颜色是 \(0\) 还是 \(1\),权重之和的最大值。转移时做树形背包即可。
这样子时间复杂度是 \(\operatorname{O}(nX^2)\) 的,无法通过。虽然这个转移是个 max+ 卷积形式的,但很遗憾没有凸性,不能直接闵可夫斯基和。
状态上难以优化,只能从转移下手。不过在优化前不妨先考虑以下一个简单的问题:
给定一棵树,每个点有权值和重量。如果选择点 \(u\),那么 \(u\) 的父亲也要选,求总重量不超过 \(X\) 的情况下总权值的最大值。
这其实就是树上依赖背包。这个东西的 \(\operatorname{O}(nX)\) 做法是 将父亲 dp 值传给儿子作为儿子的 dp 初值。这样子就不需要对于每个儿子做一遍 max+ 卷积,只需要在最后算上必选自己的代价即可(不理解可以先专门学一下这个东西)。
回到这道题,受上面那个东西的启发,我们可以类似的考虑在本题中计算点 \(u\) 的 \(f\) 时把前一部分的儿子的 dp 值传给下一个儿子。
但这样会出现一个问题,如下图所示:

其中蓝色和红色表示不同的颜色的点。
那么对于这个红色点,我们之前已经计算了其父亲的左边的子树,得到了一个 dp 值作为初值。但假如在左边我们已经选择了一个蓝色点,此时右边的红色点选择的时候就有可能出现 既选择了红色点又选择了蓝色点 的情况。而我们这时状态只记录了红色点,导致我们的 dp 出现了问题。(可能有点绕,可以自己画图理解一下)
考虑解决这个问题。思考一下会发现我们实际上只需要把不同颜色的 dp 分别传给儿子进行迭代即可,具体的,在计算 \(f_{v,i,0}\) 的时候只把 \(f_{u,i,0}\) 传进去 替代原本要传的两个数组。这样就保证了前面的那些子树中选择的点的颜色不会产生冲突。
考虑时间复杂度。由于每个点的每个儿子都要向其进行两遍遍历,于是时间复杂度变成了 \(\operatorname{O}(2^nV)\),甚至不如暴力。
我们考虑那些地方很浪费时间。对于一棵树,我们不难发现遍历的第一条链我们传入的 dp 初值全部都是 \(0\)。更一般的,不难发现对于每一个子树内的第一条遍历的链,其不同颜色所对应的数组的值实际上是一样的!
于是可以直接重链剖分。对于重儿子我们只需要遍历一次,而对于亲儿子则和之前一样暴力。
考虑时间复杂度,设 \(F(x)\) 表示 \(x\) 个点时的时间复杂度,\(h_x\) 为 \(x\) 的子树中选择了的重儿子的子树节点数,有:
根据主定理,这个东西的复杂度是 \(\operatorname{O}(n^{\log_2^3}X)\),大约为 \(\operatorname{O}(n^{1.59}X)\)。
由于传进去的 dp 初值不一样,所以现在我们只能对一个节点计算答案。考虑拓展到多个节点。
还是重链剖分,对于一条重链我们都可以一遍 dfs 计算答案,对于轻儿子则直接暴力计算答案。和上面的分析方法一样,我们可以得到这个算法的时间复杂度也是 \(\operatorname{O}(n^{1.59}X)\),可以通过本题。具体实现可以看代码。
[HNOI/AHOI2018] 转盘
好题。
首先注意到我们可以在起始点等待一段时间然后一次性走完全程,这和直接走是等价的。
想要确定初始的等待时间并不好做,考虑反着来,从某一个点一直后退,最终一直停在某个点。
假设从点 \(i\) 开始后退,考虑断环成链,对于任意的 \(j\) 满足 \(i-n+1\le j\le i\),有以下式子:
将带 \(j\) 的项移到一起,有:
令 \(a_j=T_j-j\),那么我们能取得的 \(t\) 的最小值就是 \(\max_{j=i-n+1}^{i} a_j + i\)。
在最外边套上一层 \(i\),可以得到答案为 \(\min_{i=n}^{2\times n}\max_{j=i-n+1}^{i} a_j+i\)。
这样可以做到单次 \(\operatorname{O}(n)\)。
由于我们采用了断环成链,所以有一个很好的性质是 \(a_i=a_{i+n}+n\),所以可以将答案式改写为如下式子:
观察该式子的性质,不难发现最优的 \(i\) 的位置一定是某个后缀最大值的后一个位置,于是可以考虑维护一个栈记录后缀最大值。
考虑用线段树来维护后缀的最大值,但我们显然不能对于每个节点把该节点对应的区间记录下来。所以考虑只记录最大值。在合并两个区间时,左区间答案可以直接取得,但右区间答案不一定是最好的。但注意到想要取到最小值肯定是以最右侧最大值作为后缀最大值,且选择的 \(i-1\) 在左侧,于是直接二分找到那个位置即可,时间复杂度 \(\operatorname{O}(n\log^2 n)\)。
Twin Contests
考虑枚举 \(n\times p_n=s\)。不难发现由于 \(1\times p_1\le n\),所以 \(s\) 最多只会到 \(n\)。
定义 \(A(s)\) 表示对于任意的 \(i\times p_i\geq s\) 的方案数,\(g_{i,s}\) 表示在 \([1,n]\) 中满足 \(i\times x\geq s\) 的 \(x\) 的数量。那么有:
考虑快速对 \(s\) 维护增量。设 \(G_{i,s}\) 为满足 \(i\times x\geq s\) 的 \(x\) 构成的集合,那么从 \(G_{i,s}\) 转移到 \(G_{i,s+1}\),只有 \(\dfrac{s}{i}\) 有可能被删去。所以 \(g_{i,s}\) 只有在 \(s\) 是 \(i\) 的倍数时会被更新,一共只会被更新 \(\operatorname{O}(N\log N)\) 次。
接着设 \(h_{i, s}\) 表示 \(i\times p_i=s\) 并且对于 \(\forall j\neq i,j\times p_j>i\times p_i\) 的方案数,那么有:
有个很神仙的事情是这个 \(h\) 可以 \(A\) 进行对应,并且对应的系数是 \(1:g_{n,s+1}-n+1\),具体证明如下:
对于 \(h\) 的一个排列 \(p\),可以选择一个 \(m\) 使得 \(m\times p_m>s\) 的 \(m\in [n+1, N]\),交换 \(p_n\) 和 \(p_m\) 就可以得到 \(A\) 中的一个排列。
所以说在更新 \(A(s)\) 和 \(g_{i,s}\) 的时候同时更新 \(h\) 并进行统计即可。时间复杂度 \(\operatorname{O}(N\log N)\)。
And DNA
非常好的一道题。
考虑先对一些特殊的 \(m\) 进行求解。
设对于一对 \(n\) 与 \(m\) 的答案为 \(F_n(m)\)。
- \(m=0\)
既然题目要求 \(a_i+(a_{i-1}\And a_{i+1})=m\),又有 \(a_i\geq 0\) 且 \((a_{i-1}\And a_{i+1}) \geq 0\),那么必然有 \(a_i=0\)。所以此时方案数为 \(1\)。
- \(m=1\)
考虑分类讨论一下 \(a_i\) 的值。
2.1. \(a_i=1\)
那么 \(a_{i-1}\And a_{i+1}=0\),也就是不能有连续三个 \(1\)。
2.2. \(a_i=0\)
那么 \(a_{i-1}\And a_{i+1}=1\),即 \(a_{i-1}=a_{i+1}=1\),也就是不能有相邻的 \(0\)。
用矩阵乘法可以做到 \(\operatorname{O}(\log n)\)。
接下来考虑一些更加一般的情况。
- \(m\geq 2\) 且 \(m\) 为偶数
考虑 \(a_i\) 的奇偶性。
1.1. \(a_i\) 为奇数
那么 \(a_{i-1}\And a_{i+1}\) 也为奇数,也就是 \(a_{i-1}\) 和 \(a_{i+1}\) 都是奇数,归纳可得此时数组中全部都是奇数。由于两个奇数加起来产生了进位,所以这种情况下的方案数等于 \(F_n(\frac{m}{2}-1)\)
1.2. \(a_i\) 为偶数
同理,这时 \(a_i\) 全为偶数,方案数就是 \(F_n(\frac{m}{2})\)
综上所述,\(F_n(m)=F_n(\frac{m}{2})+F_n(\frac{m}{2}-1)\)。
- \(m\geq 3\) 且 \(m\) 为奇数
这个时候可以拆位成 \(\frac{m-1}{2}\) 和 \(1\),对这两个部分分别求解即可,也就是:
把 \(F(1)\) 视作常数提前计算,那么每次递归下去问题规模会缩小一半。进行记忆化,总时间复杂度 \(\operatorname{O}(\log n+\log m)\)。
Petrozavodsk Winter 2020. Day 9. Yuhao Du Contest 7 F. Fast as Ryser
神仙科技题。
先对于 \(n\) 为奇数的情况加入一个没有连边的点,这显然不影响答案,接下来考虑 \(n\) 为偶数的情况。
首先考虑如何描述“匹配”这个东西。这时有一个非常抽象的想法,对于每一对 \((i,i+1)\) 满足 \(i=2k+1,k\in \Z\),将这一对点间连一条边,我们称其为虚边。
考虑加入了这 \(\frac{n}{2}\) 条边后有什么性质。不难发现因为一个点不能连上两条边,所以最终图上的点一定只连了一条虚边或者是连了一条虚边和一条实边。再推广一下,最终的匹配和虚边结合在一起的图一定可以由若干条不交的极大的链和环拼接起来!
接着考虑链和环的形态,如图:

可以发现对于一个 \(2\times x\) 个点的环,其实边数量为 \(x\),但对于一个 \(2\times x\) 个点的链,其实边数量只有 \(x-1\)。也就是说,图中每多一条链就会少一对匹配,即设链数为 \(x\) 匹配数量为 \(\frac{n}{2}-x\)。
于是这个时候可以从边入手,考虑对于环和链进行计数。
对虚边进行状压。设 \(f_{i,s}\) 表示以 log(lowbit(s)) 那条虚边作为出发边,最后一个到达的点为 \(i\),经过的虚边集合为 \(s\) 的方案数。\(g_{i,s}\) 表示经过的虚边集合为 \(s\),最后一个点为 \(i\) 的方案数。转移枚举上一条虚边即可。
考虑计算出了 \(f,g\) 后有什么用。不难发现对于一个虚边集合 \(s\),其能构成的环的数量可以通过 \(f\) 计算,而其构成的链的数量可以通过 \(g\) 进行计算。不妨设 \(F(s)\) 表示集合为 \(s\) 的虚边构成的环的数量,\(G(s)\) 表示集合为 \(s\) 的虚边可构成的链的数量,那么有:
看到并起来等于全集,两两交为空可以想到集合幂级数。令 \(H(x)=\sum_{S\subseteq{\{1,2,\cdots,n\}}}(F(S)+\frac{G(S)}{c^{|S|}})x^S\),有:
注意到后面这个东西就是 \(e^{H(x)}\),用集合幂级数 exp 优化即可。
[ARC147D] Sets Scores
神仙数数题。
考虑确定了每次变化是否出现的数和集合 \(S_1\),设 \(A_p(i)\) 表示对于一个数列 \(p\),\(i\) 在 \(S_1\) 中,其在所有集合中的出现次数。\(B_p(i)\) 表示 \(i\) 不在 \(S_1\) 中 \(i\) 在所有集合中的出现次数。不难发现有 \(A_p(i)+B_p(i)=n\)。
考虑对于一个 \(p\) 求出所有 \(S_1\) 的代价之和。考虑 \(i\) 是否在 \(S_1\) 中出现,若出现则代价为 \(A_p(i)\),否则为 \(B_p(i)\),那么总权值就是 \(\prod_{i=1}^{m}(A_p(i)+B_p(i))=n^m\)。
所以说对于每个 数列 \(p\),其权值都为 \(n^m\)。因为 \(p\) 有 \(m^{n-1}\) 种,所以总权值就是 \(n^m\times m^{n-1}\)。
AT_dwacon6th_prelims_c Cookie Distribution
好题。
不难发现乘上后面那一堆组合数后的期望值其实就是所有方案的 \(\prod c_i\) 的和。
考虑组合意义,考虑已经知道了 \(c\) 是什么如何计算。考虑对于每个人从其选择的饼干中钦定一个出来,不难发现我们钦定的方案数就是 \(\prod_{i=1}^{n}c_i\),所以只需要计数分配的方案数即可。
考虑对于每个饼干确定其被钦定的次数。如果第 \(i\) 个被钦定了 \(x_i\) 次,那么贡献就是 \(\prod_{i=1}^{k}\binom{n-x_i}{a_i-x_i}\)。最后要乘上对人分配数量的方案数,即 \(n!\prod_{i=1}^{k}\frac{1}{x_i!}\)。
令 \(G_x(i)=\binom{n-x_i}{a_i-x_i}\times \frac{1}{x_i!}\),那么答案就是为:
注意到 \(G_x(i)\) 只与 \(x_i\) 有关,考虑 dp。设 \(f_{i,j}\) 表示前 \(i\) 个数,\(\sum_{k=1}^{i}x_k=j\) 的方案的权值和。转移枚举一个 \(x_i\),时间复杂度 \(\operatorname{O}(n^2k)\)。
[ABC279Ex] Sum of Prod of Min
个人感觉挺厉害的一道题,但题解区有人说是简单生成函数练习题......
这种题一看没有什么好的性质,组合意义转也不太行,考虑直接暴力拆式子。
发现题目有限制 \(\sum_{i=0}^{n}s_i=m\),且最后的代价是乘法之和,这很生成函数,可以考虑用生成函数推。
记 \(F_p(x)\) 代表了对于 \(p\) 这个位置填了 \(1,2,\cdots,n\) 分别会乘上多少的代价,有:
这个式子不好做,考虑将的贡献拆开,将取 min 操作变为一个减法:
继续推一下:
于是答案就是:
众所周知 \(\frac{1}{1-x}\) 相当于做一遍前缀和,所以 \(\left[x^k\right]\frac{1}{\left(1-x\right)^{2n}}\) 就是 \(\binom{k+2n-1}{k}\)。
考虑后面的东西如何化简。熟悉数学的人可能已经看出来了后面的式子就是五边形数。因为 \(n\le m\le 2n\),所以 \(\prod_{i=1}^{n}(1-x^i)\) 可以视作 \(\prod_{i=1}(1-x^i)\)。根据五边形定理,有:
由于 \(m-n\le n\),所以有用的 \(i\) 的数量是 \(\operatorname{O}(\sqrt{n})\) 的,直接枚举即可。
最后写一下完整的式子:
由于模数很小,用 Lucas 定理计算组合数即可。时间复杂度 \(\operatorname{O}(\sqrt{n}\log_{mod}n)\)。
后话:关于五边形数可以看一下 StudyingFather 大神的博客 五边形数与整数拆分问题。
CF1515G Phoenix and Odometers
非常好思维题。
一开始以为是类似同余最短路倍增搞,想了想发现不可做,考虑先找点性质。
下文用 \(u\) 代替输入中的 \(v\),\(s\) 和 \(t\) 含义与原文相同。
结论 1: \(u\) 只会经过与其在同一个强连通分量里的点。
这个结论很显然也很重要,不难发现如果到了一个不在同一个强连通分量内的点就无法回到 \(u\)。
结论 2: 强联通分量内的所有环都一定能被经过。
首先我们可以知道对于一个环与环任意一点 \(x\),设环长度为 \(len\),那么绕这个环 \(\operatorname{lcm}(len,t)\) 次后不会对模意义下的路径长度产生影响。
然后考虑证明结论 \(2\)。对于一条已经选好的路径,如果我们想要加入一个与当前路径无交的环,那么一定是找到路径上的一个点,走出去到那个环上的一个点,走完之后再回来。因为我们不想要走出去到达目标环上的那些点,而哪些点又一定构成了若干个环,所以我们一定可以在这些不想要的环上转若干圈消除贡献(就是前面说的不会影响模意义下长度)。
比如下面这张图:

其中蓝色是我们已经走了的路径,红色是我们想要获得的环,黑色是为了获得红色环需要走的环。
那么从蓝色环与黑色环相交的端点走回自己是走出了一个环的,对于这个环可以不断转圈消掉。
有了上面两个结论之后,我们就可以开始分析一下答案了。假设有 \(c\) 个环,其长度为 \(a_1,a_2,\cdots,a_c\),那么我们的目标就是判断是否存在一组 \(x_1,x_2,\cdots,x_c\) 满足:
根据裴蜀定理,该同余方程有解当且仅当 \(\gcd(a_1,a_2,\cdots,a_c)\mid-s\),所以我们只需要求出每个强连通分量内的环长度的 \(\gcd\) 即可。
但是这个东西直接做还是很困难,考虑继续找性质。
结论 3: 只有有正好一条非树边的简单环是有用的。
这个结论真的是人能瞪出来的吗。
首先需要铺垫一个较为简单的性质。在一个强连通分量内,对于一条边权为 \(x\) 的边 \((u,v)\),一定存在一条从 \(v\) 到 \(x\) 的路径使得其权值和为 \(-x\) (模意义下)。
证明很简单,对于一个环转很多次之后停在 \(v\) 就可以了。
考虑一个有多条非树边的环。由于这些非树边在 dfs 树上一定在同一条链上,所以说将这个环搞出来之后一定是一条链上面接了一些向前连的边。由于肯定是一个简单环,所以可能的情况主要有下面两种:
- 由一条主链和若干个返祖边首尾相接构成
如图:

这显然可以拆成若干个环。
- 由一条下到底的边和若干个不交的返祖边构成

前面说到,对于一个边权为 \(x\) 的边 \((u,v)\),可以视作有一个边权为 \(-x\) 的边 \((v,u)\)。那么走下去再沿着反向的树边走上去会有负贡献,而对于返祖边沿着树边走上去会有正贡献。两个加在一起消掉就只剩下路径贡献了。所以等价于若干个环拼在一起。
剩下的情况其实可以类似于这两种,所以说我们证明了结论 \(3\) 成立。
有了结论 \(3\) 后就很好做了。对于每个强连通分量把环的边权计算出来,最后求 \(\gcd\) 即可。时间复杂度 \(\operatorname{O}(n\log V)\),带 \(\log V\) 是因为要求 \(\gcd\)。
P9051 [PA 2021] Wystawa
神秘凸优化题。
直接 dp 看起来不是很好做,这种最大值最小考虑先二分。假设二分最大子段和小于等于 \(x\)。
设 \(f_{i,j}\) 表示前 \(i\) 个位置,选择了 \(j\) 个 \(b\) 中的数,后缀最小的和。有转移:
直接做没啥性质,考虑搞一些单调性出来。
考虑构造一个选择的 \(b\) 中的数越多值越大的方案。假设有 \(cnt\) 个位置 \(i\) 满足 \(a_i<b_i\),若 \(k>cnt\),就将 \(a\) 与 \(b\) 数组交换,然后变为选择 \(n-k\) 个数。
考虑对于 \(a_i>b_i\) 的位置将 \(a_i\) 变成 \(b_i\) 并强制其选择 \(a_i\),将 \(k\) 加 \(1\),这样并不影响答案,而且选择的 \(b\) 中的数越多答案就越大。
做完这一些后再来 dp。大胆猜想一下可以发现这时的 dp 数组是有凸性的,可以考虑凸优化。
对于前面的操作进行讨论。实际上 dp 的转移是这样一个事情:
-
将凸包整体加上 \(a_i\)。
-
用 \(b_i-a_i\) 和每个位置的斜率进行比较,如果 \(b_i-a_i\) 比原来的斜率小就替换掉。
-
将前缀一段小于零的位置推成 \(0\)。
-
将后缀一段大于 \(x\) 的删除。
\(1\) 操作显然直接打 tag,\(2\) 操作由于 dp 数组是一个凸包可以直接插入,\(3\),\(4\) 操作由于每个数最多删除一次可以直接暴力。用 set 维护这个凸包即可。
构造方案直接将最终的那些没有删除的凸包直接加入,并将小于 \(0\) 的位置单独插入即可。时间复杂度 \(\operatorname{O}(n\log n\log V)\)。
[CTSC2011] 字符串重排
神仙题。
为了防止出现一个字符串是另一个字符串前缀的情况先在所有字符串的最后加上一个占位符。
先做第一问。注意到将字符串按照字典序排序后的排列答案一定是最大的,所以直接放到字典树上做一遍 dfs 排好序求相邻串在字典树上点的 lca 的深度即可。
然后考虑第二问,先想一下第一问这么做为什么是对的。因为在 dfs 的过程中我们相邻的两个串一定是到了最深的位置才统计的答案从(可以感性理解一下),这样可以保证最大。注意到这一点后不难发现其实只要最终的排列对应的点是字典树的任意一个 dfs 序的子序列就满足价值最大。
然后考虑最大化任务奖励。由于第 \(i\) 个任务的奖励是 \(2^i\),所以从大到小判断每一任务。观察一下性质发现对于每个点,一个任务相当于规定了其子树作为字符串末尾出现的点的 dfs 序的第一个或最后一个或者规定两个点让它们连续出现,可以直接用链表维护子树内点的顺序。一个任务能否被满足只需要讨论每个点在其父亲的子树的 dfs 序中的位置是否是第一个或最后一个,随便判断一下即可。这里细节比较多,建议自己画几个图理解一下。最后如果满足限制就将两点在 lca 下方的祖先的链表暴力合并即可,由于链表大小是 \(\operatorname{O}(\left|\sum\right|)\) 的,不会影响复杂度。
第二问做完之后第三问就好做了。直接根据维护的链表 dfs,把顺序记下来即可。
但这样做是 \(\operatorname{O}(nq)\) 的。瓶颈在于对于一个任务所对应的串在字典树上的那两个点,我们要对其路径上的每一个点讨论其在子树的 dfn 序中的顺序。而路径长度可能会很长。
注意到如果一个点只有一个儿子那么直接把这个点连向其儿子的儿子不影响答案,所以可以删去这些点。删除了这些点后每个点的儿子数量都大于等于二,所以产生了一个深度为 \(i+1\) 的点必然有一个深度大于等于 \(i\) 的点。那么树高就变成 \(\operatorname{O}(\sqrt{n})\) 级别的了。记 \(l\) 为所有字符串的长度,时间复杂度 \(\operatorname{O}(q\sqrt{l}+l\left|\sum\right|^2)\)。
[USACO23JAN] Mana Collection P
好题。
直接做很困难,考虑找一些性质。
注意到每次到一个点会将之前的权值全部取走,所以一个点贡献的权值只与其最后一次被经过的时间有关。
将最初每个点的权值设为查询时的时间乘以点权,那么就可以反向考虑,将计算得到的权值的最大值转化为计算失去的权值的最小值。
设 \(f_{i,S}\) 表示当前在点 \(i\),经过的点的集合为 \(S\),设 \(g_{i,j}\) 为 \(i\) 走到 \(j\) 的最短路,\(v_S=\sum_{x\in S}a_x\)。枚举上一个走到的点 \(j\),有转移:
所以我们可以在 \(\operatorname{O}(2^n\ n^2)\) 的时间复杂度内求出 \(f\)。
考虑求出 \(f\) 后如何计算答案。对于一组询问 \(\{s,e\}\),其答案为:
将 \(s\) 视为一个变量,上面这个式子其实就是求若干条直线在 \(x=s\) 这个位置的截距的最大值,按照 \(e\) 分类李超树快速查询即可。
时间复杂度 \(\operatorname{O}(2^n\ n^2)\),由于答案可能很大,所以要注意一下 inf 的设置防止出现精度问题,当然也可以直接开 int128。
CF1774G Segment Covering
神秘性质题。
看到偶数减奇数首先想到了 LGV 引理,但仔细思考了一下发现没什么前途,所以选择找一下性质。
由于最终答案是奇减偶,所以可以考虑一些特殊的条件简化问题,使得这些条件的方案中选择线段个数为奇数的方案和选择线段个数为偶数的方案能形成对应。
和很多线段相关题一样,先处理包含。不难发现如果一个线段 \(x\) 包含了线段 \(y\),而又选择了线段 \(x\),那么选不选择线段 \(y\) 不会影响最终的线段的并集,但这样可以改变选择的线段个数的奇偶性,可以构成对应。所以说线段 \(x\) 不会影响答案,可以将 \(x\) 删去。
处理完包含关系的线段之后剩下的线段按左端点递增排序后右端点一定是递增的,但有了这个性质后仍然没有什么好的做法,考虑继续找性质。
考虑三条线段 \(\left[l_1,r_1\right]\),\(\left[l_2,r_2\right]\),\(\left[l_3,r_3\right]\) 选择方法的性质。根据前面的经验,我们可以找一些无用的线段删去。不难发现如果 \(l_1\le l_2\le l_3\le r_1\),那么选择了第一条线段和第三条线段后第二条线段是否选择不影响并集,而且是否选择可以改变奇偶性,所以可以删去第三条线段,并且第二条线段必选。
那么保留下来的那些线段一定都是必须要选择的,设保留了 \(k\) 个线段,如果其并集等于 \(\left[l,r\right]\), 答案就是 \((-1)^k\),否则答案为 \(0\)。
考虑加速上面这个过程。由于我们删去了线段 \(3\),线段 \(i\) 选择的下一条线段一定是一个最小的 \(j\) 满足 \(l_j>r_i\),可以用双指针预处理。
最后,在询问的时候我们只需要找到两条初始的线段,然后往后跳,根据跳的步数输出即可。要注意一下线段无法全部覆盖的情况。
[KDOI-04] 挑战 NPC Ⅲ
先把重边删除,显然不影响结果。
注意到 \(n\) 很大,但 \(k\) 很小,所以考虑将原问题转化为一个复杂度与 \(k\) 相关的问题。
首先大小为 \(n-k\) 的独立集相当于 \(k\) 个点的点覆盖,而对于一个度数大于 \(k\) 的点,如果其不选,那么其相邻的点都需要选择,肯定超过了 \(k\),所以说度数大于 \(k\) 的点必须要选,之后将其删去。
假设前面那一个操作删除了 \(c\) 个点,那么如果答案不为 \(0\) 剩下的边的数量必须要小于等于 \(k\times (k-c)\),因为剩下的点度数都小于等于 \(k\)。并且现在有用的点的数量也是 \(\operatorname{O}(k)\) 的。
对于一张图,我们选择一个点进行考虑这个点选不选。如果选那么将这个点删去,否则将与这个点相邻的点删去。接着就变成了一个子问题。
这样复杂度显然不对,考虑优化。首先处理边界情况,对于每个点度数都不超过 \(2\) 的图其只可能由链和环组成,可以组合数算,然后背包合并。
对于其它情况,我们选择度数最大的点进行考虑。设有 \(n\) 个有用点时的处理次数是 \(F(n)\),那么最坏情况下 \(F(n)=F(n-1)+F(n-3)\),使用特征根法对复杂度进行分析可以得到复杂度为 \(\operatorname{O}(\lambda^k\times k^2)\) 的,其中 \(\lambda\) 约等于 \(1.46\)。
[ARC150F] Constant Sum Subsequence
神奇分治题。
设 \(f_i\) 表示任意总和为 \(i\) 的序列都被表达出来的所需要的最短前缀是多少,设 \(nxt_{i,j}\) 表示第 \(i\) 个位置后面第一个为 \(j\) 在那个位置,有转移:
这个转移看上去就很鬼畜,不太好优化,考虑分治。
对于一个区间 \([l,r]\),我们考虑 \([l,mid]\) 中的数对 \([mid+1,r]\) 中的数的贡献,但受限于这个需要记录两个东西的 \(nxt\),我们还是不好做。
注意到分治过程中区间长度的总和是 \(\operatorname{O}(n\log n)\) 的,而转移中 \(nxt\) 的第二维在分治过程中每一次只有区间长度种,所以可以考虑枚举第二维。
接下来考虑找一些性质。
结论 \(1\): 对于所有的 \(x,y,k\) 满足 \(x<y\),如果 \(nxt_{x,k}\neq nxt_{y,k}\),那么一定有 \(nxt_{x,k}\le y\)。
证明:
首先 \(nxt_{x,k}\) 一定是小于 \(nxt_{y,k}\) 的,而如果存在 \(y< nxt_{x,k}\le nxt_{y,k}\),就代表第 \(nxt_{x,k}\) 个位置的值是 \(k\),那么 \(nxt_{y,k}\) 就会是 \(nxt_{x,k}\),违背了最初的条件。
结论 \(2\):如果 \(l\le x\le mid,\ nxt_{f_x,k}\neq nxt_{f_{mid},k}\),那么我们不需要考虑 \(f_x\) 对 \(mid+1\sim r\) 中的 \(f\) 值的影响。
证明:
首先 \(f\) 是有单调性的,所以 \(f_x\le f_{mid}\)。
根据结论 \(1\) 我们可以得到 \(nxt_{f_x,k}\le f_{mid}\),而根据定义对于任意的 \(k\), \(nxt_{f_{mid},k}>f_{mid}\),所以说 \(f_x\) 在这种情况下一定不如 \(f_{mid}\)。
考虑对于一个 \(k\) 有哪些 \(x\) 是必要的。由于 \(nxt_{x,k}\le nxt_{x+1,k}\),所以实际上是有一段区间会对后面进行转移。我们可以找到 \(f_{mid}\) 前面的第一个 \(k\) 在哪里,设为 \(p\),那么对于 \(f_x>p\) 的 \(x\) 都是有意义的。
于是我们要做的事情就变成了每次对一个区间取 \(\max\),用线段树维护即可,时间复杂度 \(\operatorname{O}(n\log n+s\log^2 s)\)。
[USACO16DEC] Robotic Cow Herd P
贪心题。
遇到这中第 \(k\) 大第 \(k\) 小的问题不是二分就是搞个堆维护。这道题 \(k\) 较小所以可以考虑使用堆维护状态。
我们将每行选到了那个位置作为状态记录下来,考虑不重不漏的对状态进行转移:
-
将当前行的位置向后移动。
-
将当前行更新为当前行加一。
-
选择最靠前的选到了第二个位置的行,将其左移,并将下一行右移。
然而这样状态还是太复杂了,考虑简化。
我们将行按照次小值减最小值从小到大排序,可以发现一定有一段后缀是 \(1\)。设 \((x,y)\) 表示 \(x\) 行选择的位置是 \(y\),且 \(x+1\sim n\) 都在第一个位置,那么转移有:
-
\((x,y)\) 到 \((x,y+1)\)。
-
\((x,y)\) 到 \((x+1,2)\)。
-
\((x,y)\) 到 \((x+1,2)\),注意只有 \(y\) 为 \(2\) 时可以转移,且更新的值与上面的不同。
用堆维护,每次取出最小值转移即可。
寒妖王
图计数题。
首先考虑确定没有消失的边集后如何找到权值最大的符合条件的边集。将边权从大到小排序,对于每条边讨论其连接的两个点的情况。如果这两个点没有联通或者其所在连通块内没有环就连上即可(类似于 Kruskal)。
不难发现如果对一个树求那么保留的边集合是一颗树,对联通图做保留的边集一定是基环树。
考虑对消失之后的权值求期望。把贡献拆到每条边上,期望最后处理,那么问题转化为有多少种方案使得这条边会被选中。
首先发现权值比这条边小的边都是不必要的,因此从大到小排序依次处理。那么一条边能被选上一共有三种情况。
下文记 \(f_S,g_S\) 分别表示点集为 \(S\) 的树和联通图的数量,\(cnt_S\) 表示点集 \(S\) 的导出子图的边的数量,\(U\) 为 \(\{1,2,3\cdots, n\}\),这些东西都可以在 \(\operatorname{O}(3^n)\) 内求出。
- 原先的两个点已经联通。
此时如果能够选这条边那么原来的这个联通块一定是一个树。因此此时方案为:
- 原先的两个点不连通且加入这条边后在前面的类
Kruskal操作中形成的联通块为树。
那么两边的联通块都是树,方案数量为:
- 原先的两个点不连通且加入这条边后在前面的类
Kruskal操作中形成的联通块为基环树。
那么原来有一个联通块是树,另一个联通块是图,讨论那一边是图即可。方案数为:
对每条边都算一遍即可,时间复杂度 \(\operatorname{O}(m\times 3^n)\),轻微卡常,有一些不必要的状态可以跳过。
可以用集合幂级数 ln 求 \(g\),用矩阵树求 \(f\) 再高维前缀和计算答案做到 \(\operatorname{O}(n^2m\times 2^n)\),快不了多少也没有必要。
dmy 2025 CSP-S 模拟赛 Day4 B. 小球进洞

\(2\le n\le 5000,0\le m\le 10^5\)
喵喵题。
首先将操作视作全部相同的,最后乘上组合数即可。
首先考虑对于一个打洞情况计算进洞情况。设 \(s_i\) 表示 \(i\) 的子树中有多少个叶子节点,\(c_i\) 表示 \(i\) 的子树中有多少个边上的洞,那么方案数为:
从叶子向根考虑,发现上面的东西的组合意义其实就是每个点有若干条路可以走,每次给一个边打洞相当于将这条边上方的所有点都多提供一个可以使用任意次的工具,那么在一个点处相当于有三种走法:
- 直接走原来位置。
那么直接将方案全部乘上 \(s_i\) 即可。
- 走一个使用过的工具。
那么直接乘上使用工具的方案,再乘上这个工具在那条边上的方案即可。
- 走一个没有使用过的工具。
直接乘上这个工具在那条边上即可。
发现工具最多只会被使用 \(n\) 次,因此可以直接 dp。设 \(f_{i,j}\) 表示 \(i\) 子树内用了 \(j\) 个工具,所有方案的权值之和。设 \(sz_i\) 表示 \(i\) 的子树大小,有转移:
最终答案为:
这里 \(\binom{m}{i}\times i!\) 是为了给操作编号,总时间复杂度 \(\operatorname{O}(n^2+m)\)。
Petrozavodsk Winter 2019. Day 8. Petrozavodsk SU Contest J. Count the Sequences
很厉害的数数题。
首先把 \(n\) 减 \(1\)。
注意到 \(x_i\le b^i-c\) 这个限制很烦,直接容斥掉用插板计算,有:
注意到确定 \(|S|\) 后 \(\sum_{i\in S} (b^i-c+1)\) 中的 \(-c+1\) 可以提出来,前面的 \((-1)^{|S|}\) 也可以确定,所以变成枚举 \(|S|\)。令 \(F(k)\) 为 \(|S|=k\) 时的答案,即:
令 \(A=n+m+k(c-1)\),\(k\) 确定时 \(A\) 是常数,有:
将组合数拆开,为了方便后续化简我们将 \(A\geq \sum_{i\in S}b^i\) 作为条件写到求和中:
考虑处理 \(\prod_{h=0}^{m-1}((A-h)-\sum_{i\in S}b^i)\) 这个式子。我们可以发现这个东西的展开式可以视作 \(\sum_{l}a_l(\sum_{i\in S}b^{i})^l\),即将 \(\sum_{i\in S}b^i\) 带入一个我们可以计算系数的的多项式里去。
于是我们现在要处理两个问题。一个是求出 \(\prod_{h=0}^{m-1}((A-h)-x)\) 这个多项式每一项的系数,另外一部分是对于每一个 \({l}\) 求出 \(\sum_{S\subseteq\{1,2\dots,n\},|S|=k,A\geq \sum_{i\in S}b^i}(\sum_{i\in S}b^i)^{l}\)。
第一部分非常简单。设 \(g_{i,j}\) 表示 \([x^j]\prod_{h=0}^{i}((A-h)-x)\) ,转移:
因此我们可以 \(\operatorname{O}(m^2)\) 求出 \(\prod_{h=0}^{m-1}((A-h)-x)\) 每一项的系数。
第二部分较为复杂。先考虑没有 \(A\geq \sum_{i\in S}b^i\) 这个限制怎么做,设 \(f_{i,j,k}\) 表示 \(\sum_{S\subseteq\{1,2\dots,i\},|S|=j,A\geq \sum_{a\in S}b^a}(\sum_{a\in S}b^a)^{k}\),有转移:
可以 \(\operatorname{O}(m^4)\) 求出。
接着考虑如何处理 \(A\geq \sum_{i\in S}b^i\) 这个限制。发现 \(\sum_{i\in S} b^i\) 实际上是 \(b\) 进制下的一个 01 串,所以我们可以把 \(A\) 变成一个 \(b\) 进制数,然后就是一个数位 dp 状物了,时间复杂度还是 \(\operatorname{O}(m^4)\)。
综上,我们可以在 \(\operatorname{O}(m^4)\) 内求出一个 \(F(k)\) 的值,因此总时间复杂度 \(\operatorname{O}(m^5)\)。
序列变换
超级难题,STO zjh OTZ。
先考虑没有修改怎么做。观察到如果一个 \(1\le i\le n-k\) 的 \(a_i\) 被修改后一定大于没有被修改的那些数,而确定哪些数被修改后的概率是确定的,所以说 \(b\) 的限制相当于是规定了前 \(n-k\) 个数中被更改的数的个数的一个下界,所以我们只需要求出前面的数中被修改了若干个的方案,最后做前缀和即可。
假设在后面 \(k\) 个数中选择了 \(x\) 个数,考虑先把这更改的 \(x\) 个数框出来,其概率乘上 \(\frac{n!}{(n-k)!}\) 为:
接着考虑计算成功赋值的概率。
结论1:令 \(a'\) 为操作后的 \(a\) 序列,对于 \(1\le i\le n-k,n-k<j\le n,P(a'_i=a_j)=\frac{1}{n}\)。
证明:
考虑每个位置指向了那里,概率为:
\[\prod_{i=1}^{k}\frac{n-i}{n-i+1}\times \frac{1}{n-k}=\frac{1}{n} \]
结论2:对于只有前面 \(l\) 个数和后面 \(l\) 个数的情况,其概率为 \(\prod_{i=1}^{l}\frac{1}{l+i}\)。
证明不会,但通过结论 \(1\) 感性理解一下是这样的。
有了这两个结论之后就可以算出确定 \(x\) 时的概率为:
对 \(ans\) 做前缀和就能得到答案。
至于 \(b\) 对选择数量的限制实际上就是找到一个最小的 \(i\) 使得 \(pos_i<pos_{i-1}\) (\(a_{pos_{i}}\)=i) 的 \(i\),用线段树和二分即可,总时间复杂度 \(\operatorname{O}(n\log n+q\log n)\)。
城市旅游
STO nzq OTZ。
第二问就是 [ROIR 2025] 旅行路线,考虑第一问怎么做。
首先有个想法是 \(f_{i,j}\) 表示第一条路径走到关键点 \(i\),第二条路径走到关键点 \(j\) 的方案数,转移直接类似于上面那道题。但是这样是不对的,应为计算关键点间的方案数时没有保证不会经过别的关键点,所以会算重,考虑设计容斥系数去计算这个东西。
结论:对于一个有 \(x\) 个关键点重复经过的路径,其容斥系数为 \((-1)^x\)。
证明:
假设有 \(x\) 个关键点被重复经过,那么此时这一对路径被计算的次数为:
\[\sum_{i=0}^{x}\binom{x}{i}\times 2^i \]带上容斥系数后就变为了
\[\sum_{i=0}^{x}(-1)^{x-i}\binom{x}{i}\times 2^i \]用二项式定理收回去就变成了
\[(2+(-1))^x=1 \]
因此,只要在 dp 时计算 \(f_{i,i}\) 的值时乘上 \(-1\) 即可。时间复杂度 \(\operatorname{O}(n+m+k^2)\)。
[UOI 2024] AND Array
和 AT_arc205_e 很像。
考虑倒序处理每个数。将 \(0\) 去掉后我们发现被计算的数最多只有 \(b\) 个,于是只需要维护下一个合法的位置即可。
将初始时的 \(x\) 变成 \(2^b-2^j\),每次操作变成减去 \(a_j\),那么实际上就是查找之后第一个为 \(x\) 子集的位置。
采用根号分治的思想,将整个数劈成两个部分。对于其中一个部分在插入那个 \(a\) 数组中的值时暴力更改其超集,另外一部分在查询时暴力枚举子集。查询的数量是 \(\operatorname{O}(nb^2)\),插入的数量是 \(\operatorname{O}(nb)\),平衡一下就可以做到 \(\operatorname{O}(nb\sqrt{2^b})\)。
P9393 紫丁香
一道每一步都很简单但拼在一起就较为困难的题。
首先观察询问发现是查询二进制下最大值,非常套路的可以从高到低枚举每一位是否选,加上之后 check 一遍是否合法。(其它题解这里说是二分但我感觉不太一样,毕竟这里直接二分是没有单调性的(?)
先考虑对于单个询问怎么做。观察枚举出来的答案,那些为 0 的位置可以随意处理,为 1 的位置就代表这个位置在最后的若干个操作中一定是一个 1 和若干个 -。这启示我们倒着考虑操作,从答案往初始状态推导。
假设当前我们要保证集合 \(S\) 中的位置全部是 1,其他位置任意。枚举一个操作,分类讨论一个钦定为 \(1\) 的位置在经过这次还原之后的状态。
下文设这一轮选择的操作的字符串为 \(T\),考虑的位置编号为 \(x\)。
-
\(T_x=\)
1那么在之后的还原过程中这一位是什么其实已经不重要了,可以从 \(S\) 中删除 \(x\)。
-
\(T_x=\)
0那么这个位置一定是
0,所以这种情况的 \(T\) 一定不能选择。 -
\(T_x=\)
-这个操作不会对最终的这一位产生影响,所以不变化。
因此我们得到了一个较为暴力的做法,每一次暴力枚举操作,进行更改。由于这样操作到无法操作的状态是唯一的,最终合法的条件就是 \(S\) 为询问串中为 1 的位置的集合的子集。实现较优秀的话或许是 \(\operatorname{O}(nmq)\) 的。
我们考虑对上述算法进行优化。设 \(g_S\) 表示集合 \(S\) 进行任意一个操作能将那些元素从 \(S\) 中删除,\(h_S\) 表示集合 \(S\) 中的位置不为 0 的那些操作中 1 的位置的并,可以发现 \(g\) 就是 \(h\) 的高位后缀或。
于是对于一个 \(S\),只需要每次将 \(g_S\) 从 \(S\) 中删去即可。
接着考虑多组询问。可以预处理 \(f_S\) 表示 \(S\) 能操作到的集合大小最小的集合是什么,转移直接删去 \(g_S\) 即可,时间复杂度 \(\operatorname{O}(nm+m\times 2^m)\)。
[ARC152C] Pivot
好难的题,被薄纱了。
这个操作的几何意义实际上就是在数轴上选一个点,然后将所有点按照这个点对称过去。
因此我们可以得到两条性质:
-
每次操作前后点之间的相对顺序要么是原顺序要么是原顺序翻转。
-
最大值与最小值的差值是固定的。
由于第二点,我们只需要计算出最小值最小能到多少,然后加上 \(a_n-a_1\) 就可以了。
考虑第一次操作对最小值的影响。假设选择 \(i\) 进行操作,那么最小值变为了 \(2\times a_i-a_n\),即对最小值贡献了 \(a_n+a_1-2\times a_i\)。
考虑反转后的影响。令 \(b_i=|a_n+a_1-2\times a_i|\),一次操作相当于将最小值加上或减去 \(b_i\)。根据裴蜀定理,令 \(d=\gcd(a_1,a_2,\cdots,a_n)\),则 \(a_1\) 的最小值为 \(a_1 \bmod d\),因此答案就是 \((a_1\bmod d)+(a_n-a_1)\)。
[ARC167C] MST on Line++
直接算不好做,考虑拆贡献。
设 \(f_i\) 表示所有方案中边权小于等于 \(a_i\) 的边的数量,考虑如何计算。
将权值小于等于 \(a_i\) 的点拉出来将下标命名为数组 \(p\)。由于联通块是树,而我们又只需要关注联通块边的数量,且有连边的两个点小于等于 \(k\) 的限制,我们一定是选择将 \(p\) 中相邻的点连边。假设 \(p\) 的长度为 \(len\),则我们要计算的是 \(\sum_{i=2}^{len}\left[p_i-p_{i-1}\le k\right]\)。
先计算 \(\left[p_i-p_{i-1}= l\right]\) 的总和。枚举一下前面 \(i-2\) 个数占了多少个位置,可以计算出后面 \(len-i\) 个数用了多少位置。其方案数为:
通过组合恒等式可以得到这个东西就是 \(\binom{n-l}{len-1}\),所以有:
而选出来的 \(p\) 和没有选出来的数是独立的,两组之间可以互相排列,所以最终将乘上 \(i!(n-i)!\) 即可算出 \(f_i\),然后将 \(a\) 排序,答案就是 \(\sum_{i=1}^{n}a_i(f_i-f_{i-1})\),时间复杂度 \(\operatorname{O}(nk)\)。
[ARC117E] Zero-Sum Ranges 2
以下的图皆引用自 atcoder 官方题解。
有点牛的 dp 题。
先考虑没有前缀和小于 \(0\) 的情况怎么做。类似于 [JOI Open 2016] 摩天大楼 / Skyscraper,观察最终的序列的前缀和长相可以发现是一个每次上升一个或下降一个的样子,如图:

注意到值相同的位置都在同一层中,考虑分层 dp,每次将一个数插到一个位置。那么当前的状态会形成若干个段,且每一个段的两端一定是当前枚举到的这个元素,因此我们不需要知道段的具体信息,只需要知道其数量。
继续观察性质。注意到因为相邻两个位置的差值只能是 \(1\) 或 \(-1\),所以假设上一次插入的是那些值为 \(x\) 的位置,那么对于一个段,其向左右延申出来的数一定是 \(x-1\),因此我们可以考虑从高位到低位进行 dp。
设 \(f_{i,j,k}\) 表示插入了 \(i\) 个数,这些数中选择两个值相同的位置的选法为 \(j\),一共分成了 \(k\) 段。转移枚举当前这个值的数量,设为 \(l\),有转移:
其中 \(\binom{l-1}{k}\) 是将这 \(l\) 个数分成 \(k+1\) 段,直接插板。添加这些数后段数为 \(l-k\) 的原因是在没有段数的情况下是 \(l-1\) 个空位,每出现一个原来有的段就会将两个位置隔开导致空位减少,所以一共有 \(l-1-k\) 个空位,也就是 \(l-k\) 段。
然后考虑存在前缀和为负数的情况。不难发现其实正负是倒着的相同的问题,枚举大于等于 \(0\) 的数量和段数即可。
时间复杂度 \(\operatorname{O}(n^5)\),可以通过。
[ARC173D] Bracket Walk
不是很难但需要人类智慧的题,没见过这个 trick 感觉一步都不会。。。
首先有一个结论:对于一个左括号数量等于右括号数量的括号序列,其一定存在一个循环位移是合法的括号序列。
证明的话直接把 ( 当作 \(1\) 把 ) 当作 \(-1\) 找到前缀和最小的那个位置作为起点就可以了。
在图上把 ( 当作 \(1\),) 当作 \(-1\),有了这个结论之后直接可以把第 \(3\) 个限制转化为找到图上一个包含所有边的环使得这个环的边权为 \(0\)。
类似于 [WC2011] 最大XOR和路径 的思想,将环视作若干个简单环的拼接,每个环的权值可以被加上任意多次。所以实际上是要所有环的一个线性组合是的这个线性组合的权值等于 \(0\)。这个条件满足如果无法满足显然是所有环都是正环或所有环都是负环。用 SPFA 或什么算法判一下正负环即可。
时间复杂度 \(\operatorname{O}(nm)\)。
[ARC150D] Removing Gacha
首先有个很经典的做法是设 \(E(u)\) 为 \(u\) 期望被选中多少次,答案为 \(\sum_{i=1}^{n} E_i\)。
树上问题还是太难了,不难发现操作后对 \(u\) 被选中的次数产生影响的的只会是其祖先结点,于是可以把树上问题变成链上问题。
接着观察性质。发现做了 \(E(u)\) 这个转化次数之后我们在计算 \(E(u)\) 的时候不需要考虑哪些好顶点会不会选中,因为在考虑 \(E(u)\) 时只要选中的点不是 \(u\) 都不会产生代价。
枚举 \(u\) 在其和其祖先节点中选中的时候在那几次被选中,假设这个链的长度为 \(x\)。那么如果已经选中过了 \(i\) 个点,选中下一个没有选择过的点的期望操作次数为 \(\frac{x}{x-i}\),而这一次操作选择这个点的概率为 \(\frac{1}{x-i}\),所以 \(E(u)=\sum_{i=0}^{x-1}\frac{1}{x-i}=\sum_{i=1}^{x}\frac{1}{i}\),预处理一下即可。
时间复杂度 \(\operatorname{O}(n)\)。
「LibreOJ Round #8」MINIM
神秘题。
先考虑暴力。题目要求根据 Nim 游戏转化之后就是要前 \(n\) 个异或起来等于 \(c\),于是可以设 \(f_{i,j}\) 表示前 \(i\) 个数选了的数异或起来等于 \(j\) 的最小代价,暴力转移。
考虑优化。观察两个数异或出来的数可能是多少,不难发现是先或起来然后在最高的一个两个数都为 \(1\) 的位以下的都变成 \(1\)。(因为可以在该位置选择一个数将其减 \(1\),这个位置还是 \(1\),但下面的位置全部变成 \(1\) 了)。
因此这个东西是有单调性的,可以设 \(f_{i,j}\) 变成原来的后缀最大值。大胆猜测在数据随机的情况下 \(f\) 值不同的段数很少,暴力维护暴力转移即可。
[ARC129D] -1+2-1
很厉害的推式子题,要多训式子了。
下面默认下标为 \(0\) 时是 \(n\),为 \(n+1\) 时是 \(1\)。
可以先非常典型的设 \(x_i\) 表示 \(i\) 操作了多少次(\(x_i\geq 0\)),那么答案就是 \(\sum_{i=1}^{n}x_i\)。
考察 \(x\) 的性质,不难发现有 \(2\times x_i-x_{i-1}-x_{i+1}+a_i=0\)。
发现上面的式子有个同构,设 \(b_i=x_i-x_{i-1}\),有 \(b_{i+1}-b_i=a_i\)。
接着考察性质,发现本题中 \(\sum_{i=1}^{n} a_i\) 如果不等于 \(0\) 一定无解,所以我们只要考虑 \(\sum_{i=1}^{n}a_i=0\) 的情况。
发现把若干个 \(a\) 加起来的结果会产生相消,所以不妨加起来:
再发现 \(\sum_{i=1}^{n}b_i\) 是等于 \(0\) 的,所以不妨把上面那个式子也加起来:
把 \(-nb_1\) 移到右边去就构造出了 \(\sum_{i=1}^{n}b_i\),于是有:
解个方程有:
接着我们就可以递推出 \(b\) 数组的所有值了。
接着考虑解决 \(x\),因为 \(x_i=x_{i-1}+b_i\),所以只需要确定 \(x_1\) 的最小值即可。
由于我们有 \(x_i\geq 0\) 的性质,所以说只要递推一遍,将 \(b\) 的前缀和中最小的数的相反数和 \(0\) 取 max 即可,具体实现细节可看代码。
总时间复杂度 \(\operatorname{O}(n)\)。
[JSC2023 Final G] Fusion
超级厉害的数数题,使我大脑旋转。
考虑最后的答案是个什么样子。注意到因为只会加上 \(1\),所以可以得到答案一定可以表达成如下形式(定义空集的 \(\prod_{i\in S}a_i=1\)):
因此我们需要求出有多少个缩边的顺序使得权值被乘起来的点的集合为 \(S\)(即求出 \(v_S\))。
考虑将点分成两种颜色,在 \(S\) 中的点为黑点,其余点为白点,并设黑点点权为原点权,白点点权为 \(1\),操作后的点权为两点点权之积(注意不是题目中的定义),考察一下操作的性质。不难发现一个没有合并过的白点和黑点是无法合并的,因为在原树中该白点的点权不一定为 \(1\),无法将其抹除。
考虑添加对合并操作的限制,可以规定只能同色合并,不能异色合并。但经过了合并的白点是可以进行合并的,所以可以规定两个白点合并可以合并成黑点,不难发现这样是不会算重的。合并方式如图:

考虑只剩下一个点的时候的求值,这个时候我们只需要将这最后一个点为黑色点的值加上即可。因为此时黑白点已经没有区别,所以在 \(S\) 为空集的时候会算重。
接着考虑对上面描述的合并方式进行 dp,由于操作的无序性,难以构造拓扑序。但可以注意到每个点在最后都会变成黑点,所以可以考虑记录每个点都变成黑点的时间。最终的集合 \(S\) 不能暴力枚举,也要 dp,记 \(s_u\) 表示 \(u\) 的子树大小,设计如下状态:
-
\(f_u\)
\(u\) 是最开始就为黑色的点,即需要保留 \(a_u\)。
-
\(g_{u,i}\)
\(u\) 最开始为白色,在子树中的第 \(i\) 次操作与子树中的点合并第一次变成黑色。(\(1\le i<s_u\))
-
\(h_{u,i}\)
\(u\) 最开始为白色,在子树中的第 \(i\) 次操作后与子树外的点合并第一次变成黑色。(\(0\le i<s_u\))
最终答案显然是 \(f_1+\sum_{i=1}^{n-1}g_{1,i}\)。
接下来就是重工业转移了,假设当前子树根节点为 \(u\),合并上来的子树根节点为 \(v\),分类讨论转移。
-
\(f\) 和 \(f\) 转移到 \(F\)。
两个都在一开始是就是黑色,考虑合并操作在那个位置,直接把两个子树内的操作序列合并即可,转移:
\[F=f_u\times f_v\times \binom{s_u+s_v-1}{s_v}\times s_v \] -
\(f\) 和 \(g\) 转移到 \(F\)。
由于只能黑色和黑色合并,所以合并时必须要 \(g\) 已经变黑,枚举 \(g\) 变黑的时间:
\[F=f_u\times \sum_{i}g_{v,i}\times (s_u-i)\times \binom{s_u+s_v-1}{s_v} \] -
\(g\) 和 \(f\) 转移到 \(G\)。
必须要在 \(u\) 变黑之后缩边,有:
\[G_i=\sum_{j}g_{u,j}\times f_v\times (s_u-j)\times \binom{i-1}{j-1}\times \binom{s_u+s_v-i-1}{s_u-j} \] -
\(g\) 和 \(g\) 转移到 \(G\)。
这个时候就要分类讨论了。
-
\(u\) 先变黑。
\[G_i=\sum_{j,k}g_{u,j}\times g_{v,k}\times (s_v-k)\times \binom{i-1}{j-1}\times \binom{s_u+s_v-i-1}{s_u-j-1} \]由于 \(u\) 先变黑,所以有限制 \(j-1+k-1\geq i-1\)。
-
\(v\) 先变黑。
\[G_i=\sum_{j,k}g_{u,j}\times g_{v,k}\times (s_u-j)\times \binom{i-1}{j-1}\times \binom{s_u+s_v-i-1}{s_u-j} \]
-
-
\(g\) 和 \(h\) 转移到 \(G\)。
这时候的缩点方式是先把 \(u,v\) 缩成一个白色点,然后把缩出来的点和一个 \(u\) 子树内的点缩点变成黑色点。
当 \(u\) 什么时候变成黑色确定之后,\(v\) 也确定了,所以只需枚举一维即可。
\[G_i=\sum_{j} g_{u,j}\times h_{v,i-j-1}\times j\times \binom{i-1}{j}\times \binom{s_u+s_v-i-1}{s_u-j-1} \] -
\(h\) 和 \(f\) 转移到 \(H\)。
\[H_i=\sum_{j}h_{u,j}\times f_v\times (s_u-j)\times \binom{i}{j} \times \binom{s_u+s_v-i-1}{s_u-i} \] -
\(h\) 和 \(g\) 转移到 \(G\)。
\(u\) 和 \(v\) 先缩点成白色点,然后和外面的点合并成黑色。
\[G_i=\sum_{j}h_{u,j}\times g_{v,i-j-1}\times (i-1)\times \binom{i-2}{j}\times \binom{s_u+s_v-i-1}{s_u-1-j} \] -
\(h\) 和 \(g\) 转移到 \(H\)。
先把 \(u\) 和其子树外的点合并成黑色,再把 \(v\) 和 \(u\) 合并,分类讨论先后顺序:
-
\(u\) 先变黑色。
\[H_i=\sum_{j,k}h_{u,j}\times g_{i,k}\times (s_v-k)\times \binom{i}{j}\times \binom{s_u+s_v-i-1}{s_u-j-1} \]此时有 \(i-j+1\le k\)
-
\(v\) 先变黑色。
\[H_i=\sum_{j,k}h_{u,j}\times g_{i,k}\times (s_u-j)\times \binom{i}{j}\times \binom{s_u+s_v-i-1}{s_u-j} \]此时有 \(k\le i-j\)。
-
-
\(h\) 和 \(h\) 转移到 \(G\)。
\[G_i=\sum_{j}h_{u,j}\times h_{u,i-j-1}\times \binom{i-1}{j}\times \binom{s_u+s_v-i-1}{s_u-j-1} \] -
\(h\) 和 \(h\) 转移到 \(H\)。
先把 \(u,v\) 缩点,然后一起和子树外的点变成黑色。
\[H_i=\sum_{j}h_{u,j}\times h_{v,i-j-1}\times i\times \binom{i-1}{j}\times \binom{s_u+s_v-1-i}{s_u-1-j} \]
直接 dp ,在转移中需要枚举 \(j,k\) 的位置时直接用前缀和优化一下即可。时间复杂度 \(\operatorname{O}(n^2)\)。
[ARC093E] Bichrome Spanning Tree
很牛的数数!感觉要对 MST 拥有很高的理解才能做出来。
考虑题目中颜色不完全相同这个限制,可以考虑枚举那条边必选,其余边任意染色计算出最小生成树的值。
设 \(v_i\) 表示第 \(i\) 条边必选的情况下最小生成树的大小,如果对于所有 \(i\) 都有 \(v_i>X\),那么答案就是 \(0\)。如果不存在 \(v_i<X\) 并且存在 \(v_i=X\),那么等于 \(X\) 的那些边不能全部涂上相同元素。假设有 \(c\) 个 \(i\) 满足 \(v_i=X\),那么答案就是 \((2^c-2)\times 2^{m-c}\)。
接下来就是三种都存在的情况,设分别有 \(x\),\(y\),\(z\) 条。首先 \(v_i>X\) 肯定随便染色都不影响答案,直接乘上 \(2^z\),对于小于 \(v_i<X\) 的只能染同色,对于 \(v_i=X\) 只需要保证不是全都和 \(v_i<X\) 染成一个颜色即可,那么此时答案为:
直接计算即可。时间复杂度 \(\operatorname{O}(nm+m\log m)\)。
CF1685C Bring Balance
结论题,下文默认 \(n\) 为原题面中的 \(2n\)。
结论 1:答案一定小于等于 \(2\)。
证明:
将 ( 视作 \(1\),) 视作 \(-1\),令 \(a_i\) 为字符串的前缀和。设 \(x\) 为 \(a\) 中最大元素所在位置,那么操作 \(\left[1,x\right]\) 和 \(\left[x+1,n\right]\) 一定满足要求。这一点可以将前缀和取出来计算。
那么只用考虑答案为 \(0,1,2\) 的情况,为 \(0\) 直接判断初始是不是合法括号序列即可,考虑为 \(1\) 的情况如何判断。
假设最左边的 \(a_i<0\) 的位置为 \(pl\),最右边为 \(pr\),那么答案区间 \(\left[l,r\right]\) 一定满足 \(l\le pl,pr\le r\)。
结论 2:存在合法区间 \(\left[L,R\right]\) 时 \(l,r\) 满足 \(a_{l-1}\) 是 \(\left[0,pl-1\right]\) 中最大值,\(a_{r}\) 是 \(\left[pr,n\right]\) 最大值的区间 \(\left[l,r\right]\) 一定合法。
证明:
考虑翻转后括号序列合法性的 check。此时的 \(a'_{r - (i-l)}=a_{l-1}+a_r-a_i\geq 0\),也就是说 \(a_{l-1}+a_r\geq a_i\)。显然 \(a_l,a_r\geq 0\)(因为可以取 \(l=1\),\(r=n\)),所以当 \(a_{l-1}\) 和 \(a_r\) 最大的时候一定最优。
因此只需要找到这样的 \(l,r\) 判断是否合法即可,不合法就选最大值构造,时间复杂度 \(\operatorname{O}(n)\)。
[AGC043D] Merge Triplets
有点困难的数数题。
考虑归并这 \(n\) 个序列能够带来什么性质。不难发现对于一个序列,如果其第一个数被加入了 \(p\) 那么这个数一定是 \(p\) 的前缀最大值,并且接下来的若干个非前缀最大值一定是该序列中的数。也就是说,按照前缀最大值将该序列分段每一段的长度不会超过 \(3\)。
接着考虑性质,发现 \([2,1,4,3,6,5]\) 这个序列是上面的结论的反例。考虑为什么,发现是因为一个连续两个的段之后需要接一个长度为 \(1\) 的段,但上面不存在长度为 \(1\) 的段。因此,我们需要长度为 \(2\) 的段数量小于等于长度为 \(1\) 的段。
上面这些条件已经充分了,考虑 dp。设 \(f_{i,j}\) 表示前 \(i\) 个数已经填好,长度为 \(1\) 的段的数量减去长度为 \(2\) 的段的数量为 \(j\)。转移考虑其后面的数的个数,乘上选择这些数的方案即可。
时间复杂度 \(\operatorname{O}(n^2)\)。
[THUPC 2024 决赛] 排列游戏
很难的数数题,需要发现的结论还是太难了。
结论 1:对于一个排列,其通过交换两个数的操作变成单位排列的最小操作数为 \(\sum_{i=1}^{n}|p_i-i|\)。
证明直接取反排列即可。
结论 2:在每次进行交换操作之后排列的逆序对数量的奇偶性不会变化。
证明:
注意到一次交换操作可以转化为若干次相邻交换,而相邻交换显然不改变操作次数,所以交换两个数不会改变操作次数。
因此我们只需要关注逆序对数量为奇数的排列。
结论 3:对于 \(\sum_{i=1}^{n}|p_i-i|=2\times k\) 的排列,奇排列数量减去偶排列数量为 \((-1)^{n+k}\binom{n-1}{k}\)。
证明:
构造映射,将一个偶排列交换一个位置后势能和不变的进行映射,那么差值就在于那些不能交换的对。
这些不能交换的环一定是由连续数字组成,如果有 \(m\) 个环答案就是 \(\binom{n-1}{m-1}\),于是总的方案数是 \(\sum_{k=1}^{n}\binom{n-1}{k-1}\)。
那么接下来的问题就很简单了,实际上我们是要求 \(\sum_{i=1}^{n}|p_i-i|=2x\) 的 \(p\) 个数。
考虑拆贡献,将 \(\sum_{i=1}^{n}|p_i-i|\) 转化为 \((\sum_{i=1}^{n}\sum_{l=\min(p_i,i)}^{\max(p_i,i)} 1)-2n\),于是可以设计状态,设 \(f_{i,j,k}\) 表示 \(1\sim i\) 中的 \(i\) 和 \(p_i\) 已经填了,其中值已经为 \(j\),前面有 \(k\) 个 \(i\) 和 \(k\) 个 \(p_i\) 尚未匹配。转移考虑当前这两个数和前面的匹配还是自己匹配还是和之后匹配即可,时间复杂度 \(\operatorname{O}(nm^2)\)。
考虑优化。注意到每次最多只能让 \(k\) 减少 \(1\),所以如果当前状态为 \(k\) 那么最终的值一定大于等于 \(\frac{k(k+1)}{2}\),所以说 \(k_{\max}\) 是 \(\operatorname{O}(\sqrt m)\) 级别的,可以优化到 \(\operatorname{O}(nm\sqrt m)\)。
[NOI2023] 桂花树
好难的题。
先考虑第一个限制,很容易发现这个东西其实就是原树要是新树的一个虚树。
先考虑 \(k=0\) 怎么做。由于限制与最大值有关,所以考虑从小到大插入每个点。在插入一个点时不能存在当前树上的两个点使得这两个点的 lca 为插入的这个点,手玩一下可以发现想要满足这个条件必须要插入点的位置是接在某个点后面当叶子节点或者插入在某条边上。
那么假设现在有 \(x\) 个点,插入方案就是 \(2x-1\),并且怎么插入是不影响之后的插入的。所以说 \(k=0\) 时的答案就是 \(\prod_{i=n}^{n+m-1}(2i-1)\)。
接着考虑 \(k>0\)。可以证明上述 \(k=0\) 时的插入方法在现在也是可以使用的,而由于祖先要小于等于 \(\max(i,j)+k\),所以对于一个点,其祖先节点中那些有除了该点的子树的点的不同种类数只有 \(k\) 种,这启发我们以此为基础状压。
先考虑一下插入的方式。发现一个合法的树的插入方式可以被视作若干次 \(k=0\) 时的插入和若干次插入一个在 \(i+1\sim i+k\) 中的树在一条边上,然后将 \(i\) 接在该点下方。于是可以把一个点 \(i\) 和一个需要填 \(i+1\sim i+k\) 中的数捆绑考虑,先创建空点,等到之后再填。
考虑 dp,设 \(f_{i,S}\) 表示插入到了编号为 \(i\) 的点,\(i-k\sim i\) 的这些点有/无没有填的空点。
转移考虑当前位置的填法和插入在边或接在点上的情况,直接转移即可。时间复杂度 \(\operatorname{O}(Tmk2^k)\)。
有个很牛的事情是这道题的做法与这棵树的形态完全没有关系,或许有所启发。
[CERC2013] Escape
先把 \(t\) 和一个虚点连边,将虚点的权值设为 \(+\infty\),那么只需要判断能得到的最大精力是否为 \(+\infty\) 即可。
注意到一个点是否经过与其子树中的精力有关,所以只能考虑 dp。
设 \(f_{u,i}\) 表示到达 \(u\) 点前精力为 \(i\),在 \(u\) 子树中最多能增加多少的精力。转移时暴力合并即可,复杂度极高。
考虑优化。观察到 \(f_u\) 肯定由一些值相同的连续段构成,而所有的 \(f_u\) 的连续段数量之和大概是 \(\operatorname{O}(n)\) 的,所以考虑用若干个 pair 来表示 \(f\)。记 \(\{x,y\}\) 表示当初始精力大于等于 \(x\) 时最终的精力可以相较于初始精力小于 \(x\) 增加 \(y\)。那么最终统计答案时按照 \(x\) 从小到大考虑,如果当前精力大于等于 \(x\) 就把当前精力加上 \(y\) 即可。
由于要保证 \(x\) 不降顺序求答案,所以可以用堆维护信息。合并两个子树的时候用启发式合并或者可并堆,重点考虑新增一个点。
假设增加的点为 \(u\),权值为 \(a_u\),分类讨论权值正负。\(a_u\geq 0\) 时直接将 \(\{0,a_u\}\) 加入堆中。\(a_u<0\) 时相当于当前子树需要大于等于 \(-a_u\) 的精力才能进入,所以对于 \(x<-a_u\) 的那些数对要将 \(x\) 加上 \(-a_u\)。
这样还不够,因为我们的 dp 数组实际上还有一个性质,当前子树内的权值小于 \(0\) 可以不走,对应的 \(f\) 为 \(0\)。然而用数对维护时不能很好的满足这个性质,所以需要将 \(y<0\) 的数对和之后的位置进行合并,如果能够使得 \(y>0\) 就插入,否则此时走该子树一定不优,全部清空即可(实际上只要判断在 \(y>0\) 的时候放入就行了,因为如果加起来都小于 \(0\) 那么堆一定是空的)。
直接用堆维护就可以做到 \(\operatorname{O}(n\log^2n)\) 了,用可并堆可以做到 \(\operatorname{O}(n\log n)\),但没必要。
[ICPC 2022 Nanjing R] 树的染色
先考虑 dp。设 \(f_{i,j}\) 表示 \(i\) 子树内深度为 \(j\) 的点全部被染成黑色的最小代价,转移时将所有儿子的 \(f\) 的第 \(j\) 个位置加上,然后再对直接操作的代价取 min 即可。
这样时间复杂度是 \(\operatorname{O}(n^2)\) 的,用长链剖分可以将第一部分做到 \(\operatorname{O}(n)\),但第二部分难以优化。
观察到不同深度实际上没必要一起计算,可以分开计算。设当前考虑的深度为 \(x\),那么对子树操作时如果只有一个儿子的子树中存在深度为 \(x\) 的点其能一次操作变黑的点是一样的,这启发我们只保留那些有至少两个儿子的子树中存在黑点的点,也就是对深度为 \(x\) 的点建虚树。
考虑虚树上两点 \(x\) 和 \(y\),设 \(x\) 为 \(y\) 祖先,\(v\) 为 \(x\) 向 \(y\) 走到的第一个点,那么 \(y\) 到 \(v\) 路径上的点在操作时影响的点都是一样的,而这些点的 \(a\) 对应的是一段连续的区间,用 ST 表快速维护即可。
[NordicOI 2024] Thin Ice
感觉还是比较难的题,想了很久才看懂题解。
考虑如何刻画一个捡金币的过程。正着走要考虑的限制很多,不如从边界位置倒序推回去,将捡金币变成放金币。
考虑这个时候的策略,对于一个可以到达且没有放置金币的位置我们一定会选择放金币,因为这样可以让之后的限制更加宽松。
考虑一个格子在当前的金币数量下能否抵达。假设从一个格子移动到了另外一个格子,那么限制就只需要满足小的那一个格子。于是我们可以在任意相邻的格子之间连边,并将边权设为这两个格子中限制更加严格的那一个(即如果这两个格子为 \((x,y)\) 和 \((x^{'},y^{'})\),就将边权设为 \(\min(a_{x,y},a_{x^{'},y^{'}})\))。
现在是一个图上问题,依旧非常不好做。但我们可以发现对于这张图上的一个环的权值最小的边,我们可以围着环绕一圈,即可以将这条边从图中删去。重复这个过程可以发现,我们实际上只需要保留这个图路径上最小边权最大的重构树。
于是问题从图上转化为了树上。由于现在我们需要模拟放置金币的过程,所以可以先二分答案,然后进行 check。
考虑 dp,设 \(f_i\) 表示 \(i\) 的子树内任意走能留下来的金币数量的最小值。转移的时候枚举每一个儿子,如果其最少留下的金币数符合当前节点的限制就说明可以向那边走,而其余子树的可放置金币数直接减去即可,最终的合法条件就是 \(f_{rt}\le 0\),时间复杂度 \(\operatorname{O}(nm\log nm)\),可以通过。
代码放一下主要的 dp 函数(\(a\) 是原题面中的 \(d\)):
int Id(int i, int j) {
return (i - 1) * m + j;
}
void InitDfs(int u) {
for (int i : g[u]) {
InitDfs(i);
s[u] += s[i];
}
s[u]++;
}
void Dfs(int u) {
for (int i : g[u]) {
Dfs(i);
if (f[i] <= a[u]) {
f[u] = min(f[u], f[i] - s[u] + s[i]);
}
}
}
bool Check(int x) {
for (int i = 1; i <= n * m; i++) {
f[i] = kInf;
}
for (int i = 1; i <= m; i++) {
(x <= a[Id(1, i)]) && (f[Id(1, i)] = x - s[Id(1, i)]);
(x <= a[Id(n, i)]) && (f[Id(n, i)] = x - s[Id(n, i)]);
}
for (int i = 1; i <= n; i++) {
(x <= a[Id(i, 1)]) && (f[Id(i, 1)] = x - s[Id(i, 1)]);
(x <= a[Id(i, m)]) && (f[Id(i, m)] = x - s[Id(i, m)]);
}
Dfs(rt);
return f[rt] <= 0;
}
[ARC142D] Deterministic Placing
一开始看错题意猜了一个结论,然而这个结论是正确的但在原题限制我不会证。。。
下文中没有指代的连通块默认指黑点组成的连通块,黑点指有棋子的点,白点反之。
这种数数题肯定先手玩找一下性质,首先可以发现只需要 \(k=1,2\) 的时候满足对于任意 \(k\) 都是满足的。
接下来考虑一下合法方案的黑点连通块的形态。首先考虑在链上的情况,将黑点组成的段提出来,那么相邻两个段之间的白点数量一定只有一个,否则一定不符合题目要求(因为这样可以往两个方向移动)。
实际上我们可以将一个白点并入到一个黑点段中去,这样每一段都一定在两端有正好一个白点,而如果我们确定了并入白点之后的段的位置,那么染色方案只有在第一段的开头还是结尾染这两种,因为不能出现相邻的白点。而此时我们只需额外保证每一段的长度大于等于 \(2\) 即可。
用同样的思路分析树上的问题,可以猜一手是在树上分成若干不相交的链连通块,并且满足上述条件,但此时会存在一些问题。可以发现如下的情况是合法的:

此时蓝色和绿色的点对应这两条分出来的链,其中每条链有一个端点为白点,此时如果绿色链中的点移动到蓝色点,那么蓝色点将无法移动,所以这两个链只能在内部移动而每个链白点位置独立,所以有 \(4\) 的系数。
继续观察,可以发现如果黑点连通块不为链那么划分出来的链的相交位置一定不能是一个链的端点和另一个链的非端点。否则将端点的那个链移动后非端点的链上的点可以填充进那个移走的位置。
那么就可以考虑 dp 了。设 \(f_{u,0/1/2}\) 分别表示 \(u\) 子树内,\(u\) 为一条向上链的链端点但该链还未结束、\(u\) 是一条向上链的端点并且该链已经结束和 \(u\) 是链的深度最浅的点的方案数。
对于 \(0\),此时其可以是一条链最下方的一个点或中间点,并且其为中间点是可以和中间点连边,所以转移为:
上面有一个系数 \(2\) 是因为一条链有两种放白点的方法。
对于 \(1\),此时链是结束位置,而且其只能与链端点有边,所以转移为:
对于 \(2\),此时需要选择两个儿子连接,转移:
用类似背包的东西递推一下就是 \(\operatorname{O}(n)\) 的了,可以通过此题。
[ARC119D] Grid Repainting 3
很有参考价值的构造题。
类似于 [ZJOI2007] 矩阵游戏 的想法,把每一行和每一列建一个点,对于一个为 R 的位置 \((i,j)\) 将 \(i\) 和 \(j+n\) 连边,那么一次操作就相当于选择一个存在相连的边的点将其边删去,并将该行/列全部变成白色。
首先对于一个连通块,我们可以求出其任意一颗生成树,然后从叶子开始一个一个删点。对于一条边,,如果深度大的那个点代表行就说明染白了一个行,同理深度大的点代表列就说明染白了一个列。这样在我们最多只会留下来一个位置没有被删,一定是最优的。
由于图是二分图,所以只要一个连通块点数大于 \(1\) 那么其一定存在保留一个代表行的点和保留一个代表列的点的方案。所以我们只需要知道保留了多少个行和多少个列即可,这一部分可以枚举确定。
构造直接按照上述的 dfs 方法构造即可,总时间复杂度 \(\operatorname{O}(nm)\)。
[COTS 2017] 模板 Z1
以前见过类似套路的题然而不会做了。。。还是太菜了。
这种涉及到区间 min/max 的数数题首先考虑简化成只有 \(0\) 和 \(1\) 的情况(即 \(h=2\)),且保证限制的 \(x=1\)。此时有个很显然的 dp,设 \(f_{i,j}\) 表示前 \(i\) 个位置,上一个为 \(1\) 的位置在 \(j\),转移时考虑 \(i\) 这一位放不放 \(1\),直接转移即可,时间复杂度 \(\operatorname{O}(n^2)\)。
这个复杂度显然是不能接受的,考虑进行优化。发现转移的时候是实际上是要将 \(j\) 为一段前缀的状态清成 \(0\)(因为这些位置不满足区间内一定要有 \(1\) 的限制),然后对于一个前缀求和,对 \(f_{i,i}\) 单点更改,可以用线段树维护整体 dp 做到 \(\operatorname{O}(n\log n)\)。
(如果不会上面这一段可以先去学一下 Intervals,或者可以根据下面那个写的比较详细的 dp 脑部一下)。
接着考虑这道题本身的做法。首先可以快速维护出每一个位置可以取到的上界,那么我们可以观察到一个性质:对于一个限制 \((l,r,x)\),所有满足 \(i\in [l,r], a_i=x\) 的 \(i\) 可填到的上界一定为 \(x\)。
考虑证明上面这个事情。对于上界小于 \(x\) 的位置其不能填 \(x\),而上界大于 \(x\) 的位置一定不会被一个限制为 \(x\) 的区间覆盖,所以说上述事情成立。
于是可以考虑把上界不同的位置和 \(x\) 不同的限制分开考虑。对于一个枚举的上界 \(v\),考虑进行和前面简化版问题类似的 dp。对于上界为 \(x\) 的一个位置 \(i\) 分两种情况:
-
没有以该位置为右端点的限制。
那么此时可以任意填,所以把每一个位置都乘上 \(x\),然后对于 \(f_{i,i}\) 求 \(\sum_{j=0}^{i-1}f_{i-1,j}\) 更改即可。
-
有以该位置为右端点的限制。
设这些限制中左端点最大的位置为 \(pos\),那么 \(0\sim pos-1\) 要被清 \(0\),而 \(pos\sim i-1\) 全部乘上 \(x\),\(f_{i,i}\) 依旧直接求前缀和即可。
上述操作可以写区间乘法区间求和线段树维护,总时间复杂度 \(\operatorname{O}(n\log n)\)。代码很长但并不难写,想清楚之后还是没什么问题的。
2025 ZR noip20 连 Day 5 T3 编码
注意到只有 0 和 1 两种字符,所以可以考虑将这些串通过 01 Trie 的形式刻画。实际上我们是要找到一个 Trie 使得这个 Trie 有 \(n\) 个叶子节点(因为没有出现前缀关系),而代价就是每个叶子到根的路径上的长度乘以该叶子节点的权值。
先考虑 \(A=B\) 的情况,此时 0 和 1 可以视作相同的,所以直接类似合并果子从叶子开始还原这棵 Trie 即可。
接着考虑 \(A\neq B\) 的情况。将 \(A=2\) 或 \(B=2\) 视作是这个位置连接的边长度为 \(2\),那么从根开始考虑,实际上每次有如下两种操作:
-
删去一个节点,将其与一个 \(a\) 进行匹配。
-
删去一个节点,设其深度为 \(x\),则加入深度为 \(x+1\) 和 \(x+2\) 的点各一个。
用 dp 来解决这个问题。设 \(f_{i,j,k,l}\) 表示对于深度小于 \(i\) 的点统计完毕,一共匹配了 \(j\) 个,深度为 \(i\) 的点有 \(k\) 个,深度为 \(i+1\) 的点有 \(l\) 个,转移分类讨论做哪种操作可以做到 \(\operatorname{O}(n^4)\)。
考虑进行拆贡献,每次将深度变为下一层时将还未被匹配的数的权值全部加上,这样就不需要记录深度了。时间复杂度 \(\operatorname{O}(n^3)\)。
[COTS 2025] 数好图 / Promet
有点厉害的数数题。
可以发现 \(k=0\) 的答案和 \(k=2\) 的答案是相同的,为了防止 corner case 直接不考虑 \(k=0\) 的求解。
称满足存在 \(1\rightarrow u\) 和 \(u\rightarrow n\) 的点构成的 DAG 称为主 DAG。首先考虑主 DAG 上的点满足什么性质,只保留主 DAG 上的点后 \(1\) 号点必有出度,\(n\) 号点必有入度,其余点必定有入度和出度。
那么此时 \(k=n\) 的情况就很好做了。考虑钦定必有出度,容斥入度。设 \(f_{i,j}\) 表示前 \(i\) 个点,钦定了有 \(j\) 个点入度为 \(0\),转移时考虑当前点有没有钦定入度,转移乘个系数即可。最后可以对于每个 \(n\) 用二项式反演求出 \(k=n\) 的答案。需要注意因为 \(1\) 号点必定没有入度所以最终容斥时要钦定最后一个点必选(具体见代码)。
那么现在我们可以对于任意 \(k\) 确定其主 DAG 了,考虑将剩下的点挂上去。
考虑对剩下的点分为 \(1\) 号点能到达和 \(1\) 号点不能到达两类。称主 DAG 上的点为 \(1\) 类点,剩下点中 \(1\) 号点可到达的点为 \(2\) 类点,剩下点中 \(1\) 号点不可达的点为 \(3\) 类点。
考虑不同点之间的连边。
-
\(1\) 类点
由于 \(3\) 类点不能从 \(1\) 号点到达,而 \(1\) 类点可以从 \(1\) 号点到达,所以 \(1\) 类点不能连 \(3\) 类点,因此 \(1\) 号点可以连 \(1,2\) 号点。
-
\(2\) 类点
因为其不在主
DAG上又能从 \(1\) 到达,所以其不能到达 \(n\),只能连 \(2\) 类点。同时由于该类点的定义,必须有至少一个 \(1\) 类点或一个 \(2\) 类点与其相连。
-
\(3\) 类点
其可以连向任意点,因为其不可以从 \(1\) 号点到达,所以连不连没有影响。
由于主 DAG 上的点(即 \(1\) 类点)之间的连边已经被确定,所以只需要考虑其它种类的连边。设 \(g_{i,j,k}\) 表示前 \(i\) 个点,有 \(j\) 个 \(1\) 类点,\(k\) 个 \(2\) 类点。转移考虑当前点是哪一类,根据上面的连边方式转移即可,时间复杂度 \(\operatorname{O}(n^3)\)。
考虑优化。可以发现对于 \(3\) 类点其实不在意可以连接的 \(1\) 号点和 \(2\) 类点分别有多少个,只关心其总共有多少个。于是重新设计状态,设 \(g_{i,j}\) 表示前 \(i\) 个点有 \(j\) 个 \(1\) 类点,其它都是 \(2\) 类点的连边方案。设 \(h_{i,j}\) 表示前 \(i\) 个点,有 \(j\) 个\(3\) 类点的方案。最终枚举得到 \(1\),\(2\),\(3\) 类点分别有多少个,乘上系数即可。时间复杂度 \(\operatorname{O}(n^2)\)。
细节比较多,具体可以见代码。
[COI 2019] TENIS
数据结构题。
考虑暴力,每次选择一个数和一个维度将所有那一维小于它的数对加入(即能战胜的集合),最终所有数都被选上就说明合法。时间复杂度根据实现方式为单次询问 \(\operatorname{O}(n\log n)\sim \operatorname{O}(n^2)\)。
考虑如何快速 check。将三场比赛中的选手按照名次从 \(1\) 到 \(n\) 排列,根据上面的 check 方案,可以注意到在这个表格中一定是一段前缀中出现过的选手才有可能夺冠。
假设这个前缀长度为 \(p\) ,对于每个人,设 \(l_i\) 表示其排名最靠前为多少,\(r_i\) 表示其排名最靠后为多少。如果出现了 \(l_i\le p<r_i\) 就说明 \(i\) 是可以被战胜的,\(p\) 就应该至少为 \(r_i\)。所以不存在 \(i\) 使得 \(l_i\le p<r_i\)。
直接做是 \(\operatorname{O}(nq)\) 的,考虑用数据结构维护上述条件。将第 \(l_i\) 个位置加上 \(1\),第 \(r_i\) 个位置加上 \(-1\),对其做前缀和,那么 \(p\) 就是第一个前缀和为 \(0\) 的地方。每次修改操作就是对后缀加上 \(1\),查询操作就是查询第一个为 \(0\) 的位置。用在线段树上二分可以做到 \(\operatorname{O}(q\log n)\)。
[ROI 2016 Day2] 快递服务
STOOOOOOOOOOOO cyx CCCCCCCCCCCCOTZ。
首先考虑所有路径都是从 \(1\) 开始的一条链怎么做。根据经典结论,交集最大的一定另一个端点按照 dfn 排序后相邻的两条链或者是最靠前和最靠后的两条链。直接求 LCA 计算即可。
考虑拓展。先将两条路径 LCA 不同的情况的答案求出来,可以将路径拆为 \(lca\rightarrow x\) 和 \(lca\rightarrow y\) 两种。用树形 dp 对每个子树求出可以连向该子树中任意一个点的 \(lca\) 中深度最小的和次小的,答案就是该子树根节点的深度减去次小的深度。
然后考虑 LCA 相同的情况,此时形态如下图所示:

考虑枚举 \(x\),此时的答案为 \(dep_x+dep_y-2\times dep_{lca}=dep_x+dep_{\operatorname{lca}(v_2,v_1)}-2\times dep_{u_1,v_1}\)。这启发我们维护该子树内的路径端点,并在合并两个子树时统计答案。利用启发式合并维护出子树内端点集合,每次插入一个端点前先更新答案。根据前面路径全都为 \(1\) 开始的一条链的做法,我们在 set 中按照 dfn 排序,每次查询二分找到当前插入点的前驱和后继更新答案即可。
这个时候还有一个问题。对于一条链,如果其两个端点都在当前的子树内显然不应该将其统计进答案,所以需要对于每个路径在 LCA 处将其删去。
最终时间复杂度 \(\operatorname{O}(n\log^2n)\),可以通过,细节比较多,可以看代码。
P14352 排序
赛前写题解加 rp。
题解全是看不懂的高级推法,给个排序网络的思路。
先把 \(n<k\) 的情况判掉,此时答案为 \(n!\)。
根据排序网络的经典结论,一个排列 \(p\) 合法当且仅当对于任意 \(i\),将小于等于 \(i\) 的数视为 \(0\),大于 \(i\) 的数视为 \(1\),按位置排序第 \(k+1\) 个 \(1\) 所在位置之后的所有数都为 \(1\)。
对于一个确定的 \(i\) 进行考虑,设 \(1\) 的位置序列为 \(pos\),那么上述事情等价于 \(pos_{k+1}\sim n\) 中的数全都大于 \(i\),即 \(pos_{k+1},pos_{k+2}\cdots,pos_{n-i}\) 是连续的且 \(pos_{n-i}=n\)。那么就等价于原排列上后 \(n-i-k\) 个数全部都大于 \(i\)。
PS:关于为什么是后 \(n-i-k\) 个数,原因是在 \(pos\) 序列中 \(k+1\sim n-i\) 一共有 \(n-i-(k+1)+1=n-i-k\) 个 \(1\),所以得到了上面的东西。
继续转化,可以转化为 \(\min_{j=i+k+1}^{n}a_j>i\)。注意到对于一个位置,最大的限制到其的 \(i\) 的限制是最严格的,所以只需要考虑 \(a_{i+k+1}>i\) 即可。
令 \(i\rightarrow i+1\),则 \(a_{i+k}\geq i\ ,i\in\left[1,n-k\right]\),那么考虑按照从小到大的顺序将 \(1,2,\cdots,n-k-1,n-k\) 填入序列,每次填一个数相较于上一个数会多一个合法位置,但上一个数填完后消耗了一个位置,所以 \(1\sim n-k\) 全部都有 \(k+1\) 个位置可以填,而 \(n-k+1\sim n-k\) 这些数全部都不需要考虑位置,直接选没填的位置填即可。所以最终的答案为 \(k!\times (k+1)^{n-k}\),暴力求逆元,用快速幂计算式子后半部分即可。
[PA 2024] Desant 3
好怪的数数,不过这种套路好像挺常见(?
和 P9393 的想法有点像,可以看一下。
首先可以发现这个题的数据范围及其古怪,不是超级 dp 就应该是一些胡乱的复杂度的做法。但这题完全没有可以 dp 的拓扑序,所以考虑状压,然后用一些奇怪手法把复杂度降下来。
枚举每个位置是 \(0\) 还是 \(1\) 显然没有什么前途,考虑按照顺序根据 \(m\) 个操作把状态填出来。对于每一位,我们设其有 \(0\),\(1\),\(-1\) 三种状态,其中 \(0\) 表示没有准备好,\(1\) 表示已经准备好,\(-1\) 表示还没有确定。最终统计答案时由于一个初状态对应的末状态是唯一的,所以直接枚举为 \(1\) 的答案区间的左右端点,如果为 \(1\) 的位置全部在这个区间中并且区间中没有 \(0\) 就直接计入答案。
考虑对这个状态进行转移,设当前操作为 \(\left(x,y\right)\),\(x\) 和 \(y\) 的位置的权值分别为 \(a,b\)。
-
\(\min(a,b)\ge 0\)。
直接模拟操作向后转移即可,这样是从 \(1\) 个状态转移到 \(1\) 个状态,不影响答案。
-
\(a=-1,b=0\) 或 \(a=1,b=-1\)。
考虑 \(a=-1,b=0\) 的情况。那么可以发现如果确定后的状态中这两个数全为 \(0\) 那么这一次交换还是不交换不影响答案数量与状态正确性,而一个为 \(1\) 一个为 \(0\) 时需要交换,所以直接视作交换即可,将其变为 \(a'=0,b'=-1\)。
\(a=1,b=-1\) 同理。
这时状态数不会增加。
-
\(a=-1,b=1\) 或 \(a=0,b=-1\)
此时无论为 \(-1\) 的数实际上指代的是什么都无需交换,这时状态不变,状态数不增加。
-
\(a=-1,b=-1\)
这时分类讨论其真实对应的值的四种情况即可,这时状态数会变为原来的 \(4\) 倍。
考虑这样转移的复杂度,前 \(3\) 种转移显然不影响复杂度,而第 \(4\) 种转移会让状态数乘 \(4\),\(-1\) 的个数减少两个。所以总时间复杂度是 \(\operatorname{O}(2^n(m+n^2))\) 无法通过。
考虑优化。仔细读题发现有一个非常重要的东西我们没有用上,即输出种类数对 \(2\) 取模的结果。考虑现在的复杂度瓶颈处,不难发现在转移 \(4\) 中真实数值为 \(a=0,b=1\) 和 \(a=1,b=0\) 两种情况在转移后的状态是相同的,可以一一对应,所以说这两种情况可以不考虑,这样就只会转移到两个状态。时间复杂度为 \(\operatorname{O}(2^{\frac{n}{2}}(m+n^2))\)。
[JOIST 2025] 勇者比太郎 3 / Bitaro the Brave 3
非常好题目,但是不给 \(P\) 相等的特殊性质是什么意思?
先考虑对于每个询问做一遍暴力。二分答案,然后按照出现的时间顺序从前往后加入每个怪物,每一时刻选择当前存活的强度最大的怪物进行攻击进行 check。用堆维护可以做到 \(\operatorname{O}(nq\log^2n)\)。
这个东西看上去没啥前途,不过根据优先选最大攻击的思想可以想到变换顺序,按照强度从大往小填,然后尽可能填满。但每次填入一个数之后要维护有那些位置可以填,这个东西本质是维护连续段并支持合并,显然也无法优化。
上面两种做法都说明了对于单独的一个怪物我们不太能维护出其被攻击的次数,那么不妨整体考虑,求出强度大于等于一个数的怪物整体最多能有多少时间被攻击,然后拆贡献算出答案。即设 \(f_i\) 表示排序后 \(i\sim N\) 这些怪物总共被攻击多少次,答案为 \(\sum_{i=1}^{N}(H_i-H_{i-1}) f_i\)。注意这里没有考虑 \(H\) 相同的情况,实现过程中可能要写个离散化之类的处理一下。
整理一下想法可以得到一个大致的思路。将怪物按照强度从小到大排序,枚举每一个后缀求出这个后缀中的怪物在每种难度下总共被攻击的次数,然后对每一个后缀将相同难度的贡献合并起来就能得到答案。
现在的问题就是对于保留一段后缀的怪物求出它们最多被攻击的次数。可以发现现在每个怪物被攻击了多少次我们不在乎,所以可以视作它们的强度全部都为 \(1\)。
考虑解决上面这个问题。将怪物和每个时刻视为两个点集,那么将怪物与能攻击其的时刻连边,求出的答案就是这个二分图的最大匹配。而根据 Hall 定理的推论,将怪物按照出现时间排序,设 \(n\) 为当前保留的怪物数量,我们可以得到这个二分图的最大匹配:
这个式子对应到 Hall 定理中有 \(|V|=\sum_{j=i}^{n}H_j\),\(|n(V)|=T-S_i\)。
直接做会得到一个 \(\operatorname{O}(N^2L+Q\log L)\) 的东西,考虑优化。
令 \(k_i=\sum_{j=i}^{n}H_j,b_i=-T+S_i\),那么上面那个式子就是 \(\max_{i=0}^{n}(k_i \ell+b_i)\),即 \(n\) 条直线在某个位置的截距最大值。
那么就可以将这些直线拼成一个上凸壳来得到每个 \(\ell\) 会受到的贡献。用栈来维护这个凸壳,由于只有 \(n\) 条直线,所以凸壳上最多只有 \(n\) 个点。用差分分段将这些位置对应的直线加上去就好了。时间复杂度 \(\operatorname{O}(N^2+L+Q\log L)\)。
[JOIST 2025] 外郎糕 / Uiro
感觉是 JOIST2025 中除了几天签到外最可做的题了。然而因为忘记可以离线降空间并没有做出来。。。。
下文操作 \(1\) 表示加上,操作 \(2\) 表示减去。
先考虑一个暴力。每次询问从前往后考虑每一个数,能选择减就减去,不能减去就找到前面的选择了减去的数中最大的那一个看能否替换。用堆维护,时间复杂度 \(\operatorname{O}(nq\log n)\)。
这个东西对顺序的要求太严格了,不太能优化,考虑根据这个做法找一些性质出来。
性质 \(1\):最优情况中,对于一对 \(i<j\) 满足 \(a_i>a_j\),一定不会出现选择 \(i\) 为操作 \(2\) 但 \(j\) 选择操作 \(1\) 的情况。
这个东西证明是显然的,直接调整一下一定不劣。
性质 \(2\):对于一个数 \(x\),将等于 \(x\) 的位置留下来,那么最终的选择方案中进行操作 \(2\) 的那些等于 \(x\) 的位置是这些位置的一段后缀。
同样的调整一下不劣。
性质 \(3\):对于一次询问,设 \(pos_i\) 表示值为 \(i\) 的数中位置最大的进行操作 \(1\) 的数的位置,那么有 \(pos_i\ge pos_{i-1}\)。(注意这里不考虑一个值全部进行操作 \(2\) 的情况。)
同理。
注意到值域很小,我们可以编出一个大概的做法。从小到大枚举每一个值,根据前面的值的最靠后的进行操作 \(1\) 的位置二分找到当前值满足前缀进行操作 \(1\) 的最小位置即可,注意特判一下全部选的情况即可。
记二分中 check 的部分复杂度为 \(\operatorname{poly}(\log n)\) 现在的复杂度是 \(\operatorname{O}(qV\operatorname{poly}(\log n)\log n)\),因此要找到一个复杂度较低的 check 方式。
观察下面一张图,当前考虑的值为 \(x\),其中红色的段是 \(1\sim x-1\) 的那些数到大的最大位置,绿色段是当前二分到的位置,黑色为剩余。

由于红色部分已经确定,所以我们只需要考虑绿色和黑色部分其前缀是否都大于等于 \(0\)。所以我们可以根据这些限制直接算出每个前缀的和是多少,对于每一个 \(x\) 都维护,并用 ST 表查询区间最小值来 check 是否前缀都大于等于 \(0\),可以做到时间复杂度 \(\operatorname{O}(nV\log n)\)。
这样有一个问题,空间开不下,但只要离线询问枚举上面的 \(x\) 就好了。
最后再提醒一下二分位置前要先特判将这个值的那些数全部选上的情况!!!!
[BalticOI 2024] Portal
先考虑一维的怎么做。稍微思考一下就可以发现由于以每个点作为坐标轴原点时的染色要相同,所以染色方案一定是将一个颜色序列作为循环节循环,循环节长度必须是所有相邻两个位置的差的因数,所以直接取最大公因数。时间复杂度是 \(\operatorname{O}(n\log V)\)。
考虑拓展到二维。由于题目中对合法方案的 check 相当于是将平面直角坐标系的原点平移后与原坐标系完全重合,所以可以直接先选择一个点作为原点。即设点序列为 \((x_1,y_1),(x_2,y_2),\cdots,(x_n,y_n)\),那么可以选择一个 \(p\) 使得 \(1\le p\le n\),获得一个新序列 \((x_1-x_p,y_1-y_p),(x_2-x_p,y_2-y_p),\cdots,(x_n-x_p,y_n-y_p)\)。下文中令 \(a_i=x_i-x_1,b_i=y_i-y_1\),即将坐标轴原点平移至 \(1\) 号点处。
结论:对于任意三个整数 \(i,j,k\) 满足 \(1\le i,j\le n,i\neq j\),将 \(a_i\) 加上 \(ka_j\),\(b_i\) 加上 \(kb_j\) 后答案不变。
证明的话好像所有能搜到的题解都说显然,我也不太清楚,就讲个大概思路吧。
由于原点已经是一个给定的点,根据题目限制从原点沿一个方向走若干步和从另一个点沿同样方向走同样步到达的点颜色相同。而根据前面一维的做法最优方法中循环的矩阵不会出现两种颜色,所以 \((a_i,b_i)\) 和 \((a_i+a_j,b_i+b_j)\) 是等价的。同理可以得到上述结论正确。
例如下面这张图,点 A 和点 B 实际是等价的。

接着考虑根据这个性质将题目简化,将二维的问题根据这个结论变成一维。注意到 \((a_i+ka_j,b_i+kb_j)\) 类似于辗转相除法,所以可以考虑将第一维消成 \(0\)。这样我们就将这 \(n\) 个点的坐标变成 \(1\) 个 \((0,0)\),\(n-2\) 个 \((a,0)\),和一个 \((x,y)\)。按照第一维求出这个矩阵的水平长度,其竖直长度维 \(y\),两者相乘即可。



浙公网安备 33010602011771号