杂题乱做
[ARC216A] Reversi 3 (01 Ad-hoc)
人类智慧是学不会的。归类:01 序列邻位 trick,Ad-hoc。
首先两端是不会动的,因此 \(A_1\ne B_1\vee A_n\ne B_n\) 可以直接判无解。接着考虑简化这个操作。
考虑异或差分,令 \(c_i=A_{i-1}\oplus A_{i}\),那么 \(A_{i-1}=A_{i+1}\) 可以被等价转化为 \(c_i=c_{i+1}\),反转 \(A_i\) 等价于同时反转 \(c_i,c_{i+1}\)。
因此现在的操作变成,可以在序列上将 00 变成 11,将 11 变成 00。还是不够简洁。
考虑 P14015 [ICPC 2024 Nanjing R] 生日礼物 的技巧,将奇数位反转,这样相邻相同消去的操作就变成了交换相邻两位。
于是问题简化成:对于两个 \(01\) 串 \(S,T\),任意交换 \(S\) 中的相邻字符使之与 \(T\) 相同。
过于经典,若两个串 \(1\) 的数量不同则无解,否则对位匹配就是最优的。
P15952 [ICPC 2018 Jakarta R] Rotating Gears (树上连通块维护)
做出来了。有个小 trick:对树进行黑白染色后,可以方便刻画交替 \(+x,-x\)。
P11993 [JOIST 2025] 迁移计划 / Migration Plan(线段树合并,dfn序)
尝试模拟往上合并贡献的过程。我们发现这并不好维护。
考虑换维,对查询点 \(u\) 拆贡献,能贡献到它的海狸只有【当前深度为 \(dep_u\)】且【在 \(u\) 子树中的点,也就是 dfn 序上的一段连续区间】。
因此我们关注的维度只有深度和 dfn 序。考虑对每个深度开一棵动态开点线段树,下标为 dfn 序。对于深度合并 \(y\to x(y>x)\),直接将 \(y\) 中的所有贡献对位加到 \(x\) 中即可,这个可以用线段树合并解决。查询只需要在对应层数查子树的 dfn 序区间即可。
时间复杂度 \(O((n+m)\log n)\)。
P16365 [OOI 2026] Tasks from Sasha (容斥, dp优化, 虚树, 均摊)
简要题意: 对树进行染色,要求每种颜色至少用一次,且第 \(k\) 种颜色点的 \(\mathrm{lca}\) 恰为 \(x_k\)。
令 \(S_k\) 表示染成颜色 \(k\) 的点集。那么方案合法可以被表示为:
考虑如何刻画 \(\mathrm{lca}(S_k)=x_k\)。
放宽条件,直接对 \(\mathrm{lca}(S_k)\in \mathrm{sub}(x_k)\) 统计,注意到算重了儿子,进行容斥:
弱化之后的问题就是:对树进行染色,满足颜色为 \(k\) 的点全部在 \(y_k\) 的子树里。同时每个颜色有一个权值,选择 \(y_k=x_k\) 权值为 \(1\),选择 \(y_k\in \mathrm{son}(x_k)\) 则权值为 \(-1\)。
染色的方案数是容易的。我们称选择的 \(y_k\) 为关键点。考虑每个点的染色方案数,设 \(c_u\) 表示 \(u\) 点及其上方有多少个关键点,那么染色方案数就是 \(\prod (c_u+1)\)。因此将 \(c_u\) 作为信息,设 \(f_{u,i}\) 表示考虑点 \(u\) 的子树,且 \(u\) 上方有 \(i\) 个关键点,此时所有方案的 \(\prod (c_u+1)\) 之和。
- 若 \(u\not\in X\)。对应 \(f_{u,i}\leftarrow (i+1)\prod\limits_{v\in\mathrm{son}(u)} f_{v,i}\)。
- 若 \(u\in X\)。我们需要选择 \(y_k=u\) 或 \(y_k\in \mathrm{son}(u)\) 或者让 \(S_k=\varnothing\)。
- 选择 \(y_k=u\)。那么在 \(u\) 处会新增一个关键点。贡献为 \((i+2)\prod\limits_{v\in\mathrm{son}(u)}f_{v,i+1}\)。
- 选择 \(y_k=v\in\mathrm{son}(u)\)。贡献为 \({\color{red}-}(i+1)\sum\limits_{v}f_{v,i+1}\prod\limits_{w\ne v} f_{w,i}\)。可以使用前后缀积优化。
- 选择 \(S_k=\varnothing\)。贡献 \((|\mathrm{son}_u|-1)(i+1)\prod\limits_{v\in\mathrm{son(u)}}f_{v,i}\)。
直接转移,可以做到 \(O(nK)\),无法通过。
注意到 \(K\) 很小,也就是大部分都是 \(u\not\in X\) 的点,而这些点的转移是非常简洁的。我们称内部没有任何 \(X\) 点的子树为空子树。那么空子树的 \(f\) 可以直接计算得到:\(f_{u,i}=(i+1)^{\mathrm{siz}_u}\)。考虑对所有 \(x_k\) 建立出虚树,那么对于虚树上跳过的一段空子树链 \(x\leadsto y\),相当于为 \(f\) 乘上 \((i+1)^{\mathrm{siz}_y-\mathrm{siz}_x}\) 的贡献。
可以发现,需要保留在虚树中的点只有:本身是关键点,有至少两个儿子子树存在关键点。
这个虚树的规模就是 \(O(K)\) 的。但是直接处理还是不行,因为虚树上的节点还是需要 \(O(\mathrm{deg}\cdot k)\) 计算信息,如果节点上挂了很多个空子树那复杂度就又会退化到 \(O(nK)\)。
经典的,若空子树的大小总和为 \(O(m)\),那么本质不同的大小只有 \(O(\sqrt{m})\) 个。设 \(e_u\) 表示点 \(u\) 的空子树大小总和,那么总时间复杂度就是 \(\sum\limits_{u=1}^{|T|} \sqrt{e_u}\),运用柯↑西↓不↑等式:
因此时间复杂度为 \(O(K^2+K\sqrt{Kn})\)。需要使用光速幂。
P8352 [SDOI/SXOI2022] 小 N 的独立集 (dp套dp, 状态优化)
把最大独立集的 \(\mathrm{dp}\) 扔进状态,记为 \(f(u,v_0,v_1)\),分别表示不选、选的最大代价为 \(v_0,v_1\) 时赋值的方案数。
可以看作是两个一维树上背包合并,因此这样做时间复杂度 \(O(n^4k^4)\)。无法通过。然后不会了。
对于 dp 套 dp,考虑状态优化,考虑 \(v_0,v_1\) 所具备的性质。发现 \(v_1\le v_0\) 的状态中记录 \(v_1\) 是不必要的,因为 \(v_0\) 限制更少并且优于 \(v_1\)。进一步地,我们发现 \(v_0\ge v_1-w_u\),因为 \(1\) 状态把 \(u\) 扔掉就能得到 \(0\) 状态。因此 \(v_1\le v_0+w_u\),而 \(w_u\) 只有 \(O(k)\) 级别。
考虑换成 \(f(u,v_0,i)\) 表示 \(v_1=v_0+i\) 的状态,特别的 \(i=0\) 表示 \(v_1\le v_0\)。转移就是:
这样变成一层树形背包,时间复杂度 \(O(n^2k^4)\)。
小鸟杯 T4 / Message(字符串,dp优化)
容易刻画“删掉开头和结尾的字符 \(c\)”,假设我们决策要删除一个字符 \(c\),如果 \(T\) 的前缀和后缀已经出现了 \(c\) 那么这个 \(c\) 是不能被删除的。令 \(L_c,R_c\) 分别表示 \(c\) 在 \(T\) 中第一次和最后一次出现的位置(因为实现的具体细节 \(L_c\) 其实是 \(-1\) 的)设计 \(f_{i,j}\) 表示让 \(T_{1\sim i}\) 和 \(S_{1\sim j}\) 进行匹配时的最小价值,得到:
直接转移,时间复杂度 \(O(n^2)\)。这部分是简单的。接下来就比较神仙了。
我们在位置 \(i\) 时记 \(\mathrm{Valid}(i)=\set{c\mid i\le L_c\vee i\ge R_c}\)。我们发现,\(\mathrm{Valid}(i)\) 只有 \(O(|\Sigma|)\) 种。
我们按 \(L_c,R_c\) 划分整个序列,也就是进行离散化,这样划分出来的每个段内部的 \(\mathrm{Valid}(i)\) 都是相同的,如果我我们尝试将每个块内优化到线性,这样时间复杂度就可以做到 \(O(|\Sigma|\cdot n)\)。
我们发现转移的两个条件 \(S_j=T_i\),\(i\le L_{S_j}\vee i\ge R_{S_j}\) 不是独立的,这使得我们 \(\mathrm{dp}\) 的转移比较杂乱。
但是再仔细观察,我们发现 \(i\ne L_{T_i}\wedge i\ne R_{T_i}\) 时这两个状态一定只有一个成立。因此将 \(i=L_c\) 或者 \(i=R_c\) 的位置单独抽出来暴力转移,对于不在两端的段内部,\(\mathrm{dp}\) 的两个转移条件是相互独立的。考虑段 \([l,r]\) 内部的转移,也就是我们尝试线性做到 \(f_{l-1,*}\to f_{r,*}\) 的转移。
考虑将所有 \(S_j\not\in \mathrm{Valid}\) 的位置 \(j\) 标记为 \(1\),\(S_j\in \mathrm{Valid}\) 位置标记为 \(0\),将 \(1\) 位置的单独拎出来作为一个新的字符串 \(S^\prime\)。可以发现,我们原来的 \(\mathrm{dp}\) 就是将 \(T[l,r]\) 作为连续子段在 \(S^{\prime}\) 上进行匹配,代价就是中间跳过的 \(0\) 的代价和。看到匹配,不难想到 KMP,用 KMP 找到 \(S^{\prime}\) 上所有的匹配位置,并用前缀和预处理 \(0\) 的代价,这样可以做到每次 \(O(1)\) 转移。因此可以做到单块 \(O(n)\),总时间复杂度 \(O(|\Sigma|\cdot n)\)。
P11803 【MX-X9-T7】『GROI-R3』此花绽放之时(树链剖分)
考虑如何处理连通块整体加,找到 \(x\) 所在极大颜色连通块的最高点作为代表元,对其打上一个 tag \((c,w)\) 表示该点往下的极大 \(c\) 连通块会 \(+w\)。查询某点信息时,需要将其祖先链的 tag 全部下传,类似一个 pushdown 的过程,向这条链外侧的儿子贡献 tag。
但是直接暴力 pushdown 显然是爆炸的,由于是树链操作套路地考虑树剖。首先需要涉及到颜色的连续段,在每条重链上开一个 ODT 来维护颜色的连续段。树剖后,我们希望优化掉重链内部的下传。打标记时把代表元所在重链的颜色连续段打上 \((c,w)\) 的标记。可以通过对每种颜色开一棵动态开点线段树做到快速打标记。
这样我们下传时只需要在轻边处理标记的转移即可。注意由于在轻边 pushdown 时只下传了一个方向的轻边,我们不应将标记清空,否则其他轻子树将无法收到 tag。但是这样可能会出现重复的标记下传,因为我们令 \(undo_{u,c}\) 表示 \(u\) 点上一次已经被父节点轻边下传时颜色为 \(c\) 的 tag 值,则每次扣除 \(undo\) 才是真实的 tag 值。
这样我们得到了一个扩展性非常强的 \(2,3\) 做法,现在考虑加入 \(1\) 操作。为了防止颜色覆盖导致 \(u\leadsto v\) 路径上方的 tag 无法正确下方,我们先把 \(1\leadsto u\) 和 \(1\leadsto v\) 的 tag 全部下放到路径之下。然后用树剖在路径上 ODT 颜色覆盖即可。注意有一个小细节:在轻边处父节点颜色为 \(c\) 的 tag 将会无效,如图:
我们发现在路径方向的轻边被错误下传了 tag,因此用 \(undo\) 消除 tag 的影响即可。
[ARC229D] Nim_k ?(博弈论)
发现操作的特殊性:每次操作后一定有一堆石子保持不动。
-
\(\sum A_i<K\) 的情况下先手必败,这是必败态。
-
\(\sum A_i\ge K\) 时,如果 \(\min A_i<K\) 那么先手必胜。这是必胜态。
-
分析到这里卡了。继续往前推,在 \(\forall A_i\ge K\) 时,令 \(B_i=A_i-K\),我们声称 \(B\) 的博弈结果等价于 \(A\)。因为如果先手操作 \(A\) 使得 \(\exists A_i<K\),由于每次操作后会有一堆不动,此时一定有 \(\sum A_i\ge K\),于是先手就必败了,因此双方都一定会保持 \(B_i\ge 0\),可以发现这就是原来的游戏。于是我们可以直接将 \(A_i\) 减到基态判断即可。
P5609 [Ynoi2013] 对数据结构的爱(数据结构,线段树)
分段函数合并。其思想是在线段树上维护分段函数,这样求 \(f_{[l,r]}(x)\) 的值就可以拆成 \(O(\log n)\) 个区间的函数复合。
对于节点 \(u\),令 \(f(x)\) 表示将 \(x\) 带入这个区间会被减多少次 \(p\),也就是经过这个区间之后的数变成 \(x+sum_u-f(x)\cdot p\),不难发现减去 \(p\) 的次数不超过区间长度,因此 \(f(x)\) 就是一个 \(O(r-l)\) 段的分段函数,可以直接暴力存分段函数的断点,令 \(c_i\) 表示最小的 \(x\) 使得 \(x\) 被减去了 \(i\) 次 \(p\),这意味着 \(x\in [c_i,c_{i+1}-1]\) 会被减去 \(i\) 次,这样空间是可接受的 \(O(n\log n)\)。如果我们可以对每个节点求出这个分段函数,查询时在每个拆出的节点上二分可以做到单次 \(O(\log^2 n)\)。
重点考虑如何 pushup。也就是我们要处理 \(f_{rc}(f_{lc}(x))\) 这个复合函数。假设初始值 \(x\) 在左半边经历了 \(i\) 次减 \(p\),则满足 \(x\in [c_{ls,i},c_{ls,i+1}-1]\),那么走完左边会变成 \(x^\prime=x+sum_{lc}-ip\),枚举 \(x^\prime\) 经过右边又经历了 \(j\) 次减 \(p\),那么需要满足 \(x^\prime\in [c_{rc,j},c_{rc,j+1}-1]\Rightarrow x\in [c_{rc,j}-sum_{lc}+ip,c_{rc,j+1}-1-sum_{lc}+ip]\),存在等价于区间有交,也就是满足 \(c_{ls,i+1}-1\ge c_{rc,j}-sum_{lc}+ip\),此时可以贡献到 \(c_{u,i+j}\),得到合并式子:
直接暴力合并是平方级别的,无法接受。常规的优化方法行不通了,考虑单调性。
令 \(f(i,j)=\max(c_{ls,i},c_{rc,j}-sum_{lc}+ip)\),我们声称 \(f(i,j)<f(i+1,j-1)\)。
可以证明 \(\max(c_{ls,i},c_{rc,j}-sum_{lc}+ip)<c_{ls,i+1}\):其中 \(c_{ls,i}<c_{ls,i+1}\) 显然,而合并的判别式 \(c_{ls,i+1}-1\ge c_{rc,j}-sum_{lc}+ip\) 变形一下就是 \(c_{ls,i+1}>c_{rc,j}-sum_{lc}+ip\)。
然后就可以优化了,在 \(i\to i+1\) 时,\(j\) 缩小一定是不优的,因此双指针就可以了,合并的时间复杂度可以优化到线性,因此合并的时间复杂度为 \(O(n\log n)\),总时间复杂度 \(O(n\log n+q\log^2 n)\)。
P5607 [Ynoi2013] 无力回天 NOI2017(根号分治,bitset)
考虑根号分治,按照集合大小根号分治。先离线下来求出每个集合最后的大小为 \(\mathrm{siz}\),称 \(\mathrm{siz}\le B_1\) 的集合为小集合,\(\mathrm{siz}>B_1\) 的集合为大集合,这样大集合只有 \(\frac{N}{B_1}\) 个。
-
\(\mathrm{siz}_a,\mathrm{siz}_b\le B_1\)。此时可以直接暴力枚举,做到 \(O(B_1)\)。
-
\(\mathrm{siz}_a\le B_1,\mathrm{siz}_b>B_1\)。对大集合维护一个桶,查询时枚举 \(a\) 内部的元素可以做到 \(O(B_1)\)。
-
\(\mathrm{siz}_a,\mathrm{siz}_b>B_1\)。自己做的时候有两个想法:
- 拆贡献,维护 \(w_{u,v}\) 表示大集合 \(u,v\) 的交集大小,这样修改是 \(O(\frac{N}{B_1})\),查询是 \(O(1)\)。
- 直接 bitset,对两个大集合的 bitset 取交,时间复杂度为 \(O(\frac{N}{w})\)。
试图结合两个做法,但是由于比较菜失败了。这样最优只能做到 \(O(N\sqrt{N})\),无法通过。
考虑在两个都是大点时继续根号分治。这次我们转换维度,从元素的角度分析。考虑按照元素的出现次数进行根号分治,出现次数 \(cnt_y\le B_2\) 的元素称之为轻元素,\(cnt_y>B_2\) 的为重元素。在处理大集合与大集合贡献的时候,对轻元素采取拆贡献策略,对重元素采取 bitset 策略。
具体的,我们对每个大点维护所有重元素的 bitset,并用 \(W_{u,v}\) 处理 \(u,v\) 两个大集合之间所有轻元素带来的贡献。
-
在 \(S_x\) 中加入 \(y\) 时
-
如果 \(y\) 是重元素,直接更新 bitset。\(O(1)\)。
-
如果 \(y\) 是轻元素,枚举所有包含 \(y\) 的大集合 \(S_z\),然后对 \(W_{x,z}\) 做出贡献。由于轻元素的出现次数不超过 \(O(B_2)\),这样枚举只会有 \(O(B_2)\) 个大集合。
-
-
在查询大集合 \(S_x,S_y\) 交集大小时
- 对于重元素,直接在 bitset 上算 \(\mathrm{popcount}(\mathrm{Bit}_a\cap \mathrm{Bit}_b)\) 即可,bitset 上只存了重元素只有 \(O(\frac{N}{B_2})\) 位,这样做的时间复杂度就是 \(O(N/(B_2w))\)。
- 对于轻元素,直接查询 \(W_{x,y}\) 即可。\(O(1)\)。
不难发现时间复杂度为 \(O(N(B_1+B_2+N/(B_2w)))\),取 \(B_1=B_2=\sqrt{N/w}\),时间复杂度为 \(O\left(N\sqrt{\dfrac{N}{w}}\right)\)。再分析一下空间复杂度:\(O\left(\frac{N^2}{B_1B_2\sqrt{w}}+\frac{N^2}{{B_1}^2}\right) =O(Nw)\),可以通过。
[ARC228A] Row and Col swap(计数 dp,置换环)
如何刻画“\(P,Q\) 最终仍是排列”?排列不好做,尝试转成 \(01\) 序列的问题。考虑直接钦定每种数的归属,最终要去 \(P\) 的染成 \(0\),最终要去 \(Q\) 的染成 \(1\)。那么问题转化成每种数选一个位置染 \(0\),另一个位置染 \(1\),通过 \(M\) 次操作将其变成第一行全 \(0\) 第二行全 \(1\) 的方案数。
不妨先弱化,假设我们已经确定了 \(01\) 染色的具体方案。对交换操作而言,我们只关注 \((P_i,Q_i)\) 这 \(n\) 个二元组,现在转化成 \(01\) 序列后,只有 \(4\) 种本质不同的二元组 \(00,01,10,11\)。那么我们可以 dp 求出 \(f_{i,a,b,c,d}\) 表示进行 \(i\) 次操作,使当前状态中四类二元组的数量分别为 \(a,b,c,d\) 的操作方案数。
转移比较繁琐,分论一下:
- 进行 \(\mathrm{swap}(P_i,P_j)\) 操作。组内自己换是不影响的,并且 \((00,01),(00,10),(01,11),(10,11)\) 的交换也是不影响的,以 \(\binom{a}{2}+\binom{b}{2}+\binom{c}{2}+\binom{d}{2}+ab+ac+bd+cd\) 的系数贡献到 \(f_{i+1,a,b,c,d}\),如果交换了 \((01,10)\) 或者 \((00,11)\) 会造成 \(a,b,c,d\) 的变化。
- 进行 \(\mathrm{swap}(Q_i,Q_j)\) 操作。与 \(p_i,p_j\) 的交换类似,不再赘述。
- 进行 \(\mathrm{swap}(P_i,Q_i)\) 操作。那么 \(00,11\) 换完之后不变,\(01,10\) 换完之后改变。
然后你发现复杂度 \(O(n^4m)\) 爆炸了。容易发现 \(a,b,c,d\) 联系是很强的,因为总共有 \(n\) 个 \(0,1\),可以得到关系式 \(2a+b+c=b+c+2d=n\),如果我们只记录 \(a,b\),可以计算出 \(c=n-2a-b, d=a\),因此状态压缩到了 \(f_{i,a,b}\),时间复杂度降到 \(O(n^2m)\),可以接受。
现在考虑加上染色怎么做。令 \(p_x\) 表示 \(x\) 在 \(P\) 中的出现位置,\(q_x\) 同理,我们可以对 \(p_x,q_x\) 一个染 \(0\) 一个染 \(1\),关注最终 \(00,01\) 的个数。令 \(g_{a,b}\) 表示 \(00,01\) 的个数分别为 \(a,b\) 的染色方案数,由于染色的位置比较杂乱难以直接转移,考虑对 \(p_x\) 和 \(q_x\) 连边,这样会形成若干置换环,不难发现在置换环上我们只关注相邻两个位置的决策,特殊处理环的开头即可,这部分也可以做到 \(O(n^2m)\)。
最后需要将两个 dp 缝起来得到答案,直接以 \(g_{a,b}\) 为初值跑 \(f\) 即可,最终答案就是 \(f_{m,0,n}\)。总时间复杂度 \(O(n^2m)\),可以通过。

浙公网安备 33010602011771号