November Rain
upd on 20260308
这是我生涯最后几个月的总结,我始终觉得我的水平已经卡到这里了,马上就要突破了,但是我没有做到上一个台阶,或者说这也可能只是我的假象,我省选打出了一个非常恶心的成绩,我失败了,就像无数次那样。这篇文章只不过是一个失败者想要留下来的一些东西,大家可以选择性的看一下,也许我失败的经验可以帮到你呢?
Cause nothing lasts forever
even cold November Rain
思维要点
自信心
自信心是一切思考的前提,如果你失去了思考的自信心那么你不可能做出来任何一道提。
永远不要在一个做法没有完全做完的时候怀疑正确性,永远要相信直觉和身体的本能。
看上去很强的必要条件,相信它是充分的
如果心存疑虑,那就不要发射
QOJ10499. Dopasowanie
在场上想到了 \(O(nmk)\) 的做法,这个时候我的问题就出在:我在排除了这个做法有能乱搞过去的可能性后,我就不知所措了,什么都不知道。我想到了一个类似于每次找 lcp 匹配的流程,但是我当时脑子里面一片混乱,根本没有意识到这个问题的难点在哪里。难点在哪里呢?难点在于:什么时候我贪心的匹配 \(t\) 是成立的,越长越好。此时我们应该考虑钦定某些条件,来强制使得它能够满足最优子结构性质。我在这里就开始大脑空白,最后光荣的获得了 50pts。
实际上其实更不是这样的,而是我在思考这个做法的时候,我就一直在反问这样做对不对?我甚至连一个完整的做法都没有给出,这太蠢了。所以在想一个连成型都没有的做法之前就思考正确性,应该把算法思考完整再来想正确性。我不太好评价这个行为。好像我经常犯这样的问题。
考虑贪心的跳 lcp,此时跳完 lcp 就会遇到三种情况,可以替换,删除或者插入。先从最朴素的做法开始,枚举左端点 \(l\),可以想到设 \(f[i, j]\) 为匹配到了 \(s[l, i]\) 内进行了 \(j\) 次操作能够匹配出来的最远 \(t\) 前缀。考虑正确性,这显然是对的,因为你早晚都要匹配,晚匹配不如现在匹配掉,晚匹配显然不优秀。
然后我们发现这样还是不行,但是注意到最多只会出现 \(k\) 个处理 \(f[i]\) 的断点,或者说 \(|f[i, j] - (i - l + 1)| \le k\)。于是 \(i - l + 1 = f[i, j] + \Delta, |\Delta| \le k\),于是可以发现这个 \(O(nk)\) 个状态里面很多地方没有值。压缩一下,设 \(g[i, j]\) 为 \(s\) 和 \(t\) 长度差为 \(i\),进行了 \(j\) 次操作后 \(t\) 最长匹配可以到哪里。因为这本质上是将 \(f[i, j]\) 中没有值的位置压缩掉了,所以正确性显然有保证。
转移用 lcp 状物转移一下就可以了,时间复杂度 \(O(nk^2)\)。
代码好像写起来细节挺多的,我被肘了好几次。
试错过程
对于一道题可能有很多个入手点,那么我们应当的姿态是什么?先把这些入手点给写下来,一个不行就换一个。可能的决策构成一棵树/DAG,于是:
要有一条路走到黑的魄力,更要有在一个“看上去可行的做法实则不可行的时候”
- 寻找不可行的地方,诊断是否致命。
- 如果的确是致命的,那么就换一个做法。
我似乎已经在模拟赛中利用过了,但是没有把它内化为自己的身体本能,所以我现在也找不到题目……
找到了。
QOJ16117. XOR-Excluding Sets
很容易把问题划归为对于每一个前缀求解异或和为 \(0\) 的子集数量和大小和。
前言:在场上做这道题的时候,我基本只会线性基板子,只记得结论是 \(2^{n - c}\) 为异或为 \(0\) 的子集数量(\(c\) 为线性基大小),即非主元数量。
直接大胆猜测,对于每一个不在线性基里面的数,可以用线性基里面的异或和表示,同时求解这个使用的集合是简单的。随后我们发现就可以独立出来每一个使用的贡献,从而求解。
这个“求解使用的集合”是简单的是我猜的,我不确定这件事情,但是我们仔细分析一下:首先是线性基内部的每一个数,显然可以维护出来,线性基外的也跟着异或,发现是容易的。
主要就是这个大胆的“猜测”。
如果一点思路都没有
- 首先,真的是一点思路都没有吗?相信自己的身体本能,相信直觉的力量。
- 如果心存疑虑,就不要发射。
- 枚举自己的技术武器。
- 从大方向到深入枚举。
遇到复杂模型
首先,严格的手玩,严格的体会过程,一定要认真对待。对于复杂的,没有见过的东西,第一件事情就是手玩一下!!!
P7830 [CCO 2021] Through Another Maze Darkly
对于一个点,第一次遇到它后,接着会走它儿子部分的一段前缀,从父亲返回。以后再遍历到这个点就是所有儿子都会遍历到。当所有点被标记,那么就会进入欧拉序的循环。
形式化的说,遍历欧拉序。首先找到一个当前没有被经过的点 \(i\),它一个儿子的前缀会被取到,这一部分一定也没有被取过,所以往里面递归,记录时间,然后跳出这个子树,寻找下一个没有被经过的点,以此类推,直到这个欧拉序被完全遍历,如果没有那么就一直进行下去。
记录这个同时计算时间。
写的代码参考了题解的,因为我觉得它写的很简洁优美。把所有的放到一个递归函数里面。
void work(int now, ll tim, int qt) {
int u = dfn[now];
if(tag[now]) {
for(int i = 0; i < pos[u].size(); i++)
del(pos[u][i]);
while(qt <= m && Q[qt].val == tim)
ans[Q[qt].ind] = u, qt++;
if(gra[u].size() == 1) work(gra[u][0], tim + 1, qt);
else work(gra[u][1], tim + 1, qt);
}
else {
int w = Find(now);
if(!tag[w]) {
while(qt <= m)
ans[Q[qt].ind] = getl(now, Q[qt].val - tim), qt++;
return ;
}
int dist = dis(now, w);
while(qt <= m && Q[qt].val < tim + dist)
ans[Q[qt].ind] = getl(now, Q[qt].val - tim), qt++;
work(w, tim + dist, qt);
}
}
对于 tag[u] = 1,即这个点没有被标记过,只找下一个位置。因为如果下一个位置没有遍历过就遍历,如果遍历过,子树内可能没有遍历完,如果遍历完了那么在出这个子树的时候自然也会到 \(u\) 的下一个儿子。总之是按照一个欧拉序转移的。
考场上我是在最后手玩链的时候模糊的感受到了这一点。但是还是没有完整的提出,最后没有写出来。我太懒惰了,没有及时手玩,这就是问题。
及时排除不可能的做法
对于某些特殊问题(比如最大独立集),如果划归到这类问题,那么显然可以考虑找性质而不是继续研究这个不可能做法。
但是有一类做法是没有这些众所周知的结论的。
鉴定一种做法不可能,一般是:这样想太复杂了,不太可能。或者说就是,如果你往一个方向想了 20min 后仍然一无所获,那么 90% 这题压根不是这个做法。及时放弃才是好选择。
P10104 [GDKOI2023 提高组] 异或图
我自己做的时候对于求解异或和为 \(0\) 方案数部分想了很久的集合幂级数……我必须承认这一点。
一般分析方法
考虑一般模型
对于一部分问题,我们拥有一个比较一般的,已经有成熟做法的模型,那么我们就先不考虑题目中可能的特殊性质,从这个一般模型出发,再来求解。这样的话我们就可以找到一个解决问题的抓手。
有一个有趣的创造者悖论,那就是有时候一个更加宏观的问题往往更好解决,这并不是偶然,因为对宏观问题的入手,往往代表着你已经深入了解了这个问题的结构。
例子:
P9169 [省选联考 2023] 过河卒
实际上只有三个点位置,所以盘面可以用六元组代表坐标,这是 \(O(n^6)\) 级别的,点数和边数同阶,为 DAG。
问题转化为在这个有向图上有一个棋子,某些点位是终止点位。获胜规则为:在终止点位上,当前手获胜;如果一个点没有出边那么必败。选择的策略是:当前手如果必胜,那么会尽可能希望行走步数少;如果先手必败,那么会尽可能希望行走步数多。
首先考虑判定必胜和必败态。经典的在 DAG 建立反图然后 dp 即可,\(f_u\) 为从 \(u\) 出发必胜/必败,然后直接转移。对于路径长度限制,注意到 bfs 反图就是按照最短路径分层的,所以对于必胜态用第一个后手必败点,必败态用最后一个后手必胜点即可,转移很自然。
总结一下,这道题我们首先分析题目转化成了很经典的,一般化的博弈论模型,然后就做完了!而且甚至一点特殊性质没有用!
CF2097F Lost Luggage
这题我没过主要是因为我被卡常了,这题思路还是挺简单的。
建立网络流模型,问题变为网络流最大流。这是一个经典的问题:分层图网络流最大流,我们考虑求解最小割,然后用状压 dp 维护连通性即可。
CF1110H Modest Substrings
很经典的 acam 上 dp,acam 过于庞大,所以对于满 trie 位置,我们可以压缩起来直接求解。
特别的要注意,这里从某种角度并不是简化了 acam 的结构,而是我们把贡献分成了两部分来计算。
结合几何图形
这是一个在扫描线中我写过的技巧,利用几何图形刻画题目条件,有的时候我们就可以成功的将困难的,不形象的条件判定转化为清晰的,容易刻画的条件,再利用各种方法求解。
很经典的应用是:将序列 \(p\) 的偏序关系用 \((i, p_i)\) 之间的点对关系进行刻画;利用凸包性质进行一些刻画。
QOJ13008 Lines Game
回到题目,因为不能重复删除,所以很显然删除的集合 \(S\) 中 \((i, p_i)\) 应当不存在逆序对。考虑判定怎样的集合是合法的,如果和 \(S\) 中一个点形成逆序对,那么就不合法。画到二维平面上,\((i, p_i)\) 作为一个点,形成逆序对说明在划分出的第二或者第四象限,那么最终 \(S\) 就会形成一个在一三象限这根轴上面走的集合,要求这根轴上面不能加入其它点,即 \(S\) 是极大的。这个可以用相邻两个 \(i\) 之间,平面的矩形中没有任何点刻画。
接下来就是对暴力 dp 的优化。如果以线性为阶段依次转移,那么就可以直接上 segbeat,这里面也有一些思考的手段在。
QOJ7649 序列
如何刻画没有 120 形态的序列?
考察这个 \(1\) 是怎么样和后面的 \(2, 0\) 形成约束的。
考察 \(a_i > a_k\) 这件事情,这要求其中的 \(a_j\) 均在小于等于 \(a_i\),发现此时刻画起来还是很吃力;再来考察 \(a_i < a_j\) 这件事情,此时要求所有后面的 \(a_k\) 都大于等于 \(a_i\)。此时我们用代数手段已经非常难以刻画了,于是使用几何手段,将序列投射到 \((i, a_i)\) 的数点上面,那么就可以这样刻画:

提取出每一个前缀严格 max 上的点,那么可以发现构成若干个矩形,这样的矩形内部也是要求满足上述的,单个矩形内部可以通过删除第一个点然后继续分型继续计算,然后和上面部分合并,这样递归下去,可以得到一个计数方法。
entertainment-1
对于序列 \(a_i\),每次询问 \(k\),构造序列 \(b_i \gets a_i + ki\),求 \(\max\{b_i\}\)
值得注意的是,在连续情况下这个等价于将一个函数的导数集体加 \(k\) 后积起来。我不太懂这个东西有没有连续函数下的高妙意义,因为我没有严肃学习过这部分内容。
回到这个问题,直接从代数手段,只能得到差分序列相加,但是用差分序列直接前缀和还原是没有前途,没办法做的。于是考虑几何角度(刚刚的连续情况其实可以启发这一点),然后发现候选点一定在一个凸壳上,然后就做完了。
找代表元
尤其在计数问题(因为要对计数结构进行优秀刻画)的时候。总之,我们经常需要用一个代表元刻画一个结构。
怎么样找代表元呢?
- 至少在计数问题中,代表元应当是唯一的。
- 代表元应当是容易求解的。
其实从中我们也可以反思一下对计数问题的解决,或者说对结构的判定。在 QOJ7649 这道题中,我们是利用递归结构来判定的;而在另外一些问题中,我们是找代表元,或是找充要条件来判定的。
CF1210F1/2 Marek and Matching
深刻,高级。
用 Hall 定理来刻画是烂大街的,问题在于如何刻画 \(V_T - \max\limits_{T\subseteq S}(|T| - |N(T)|)\)。
证明可以详见我的题解,我们可以证明满足 \(|T|\) 最小的取得 \(\max\limits_{T\subseteq S} (|T| - |N(T)|)\) 的 \(T\) 唯一。代表元不具有最优子结构,所以我们考虑正难则反。设 \(f[S, T]\) 为对于 \(S\),\(N(S) = T\) 有完美匹配的概率,\(g[S, T]\) 为 \(S\) 的代表元为 \(T\) 的概率,转移不是本文重点略去。
QOJ4812. Counting Sequence
不难想到这个问题是根号分治。我分析的时候出了问题,因为我是用 \(\max\{a\}\) 作为代表元,但是条件放宽到 \(a_1\) 做代表元是完全可行的。\(\max\{a\}\) 刻画难度显然高于 \(a_1\) 的刻画难度。
转换视角
所谓转换视角,就是一个方向行不通我们换一个方向,一个常见的换方向就是“反方向思考”。很难删除做?我们考虑加入!很难正着扫描?我们倒着扫描!
B5567 点分树
很直观的思想就是:判定一颗树 \(T\) 是否是 \(T_0\) 的点分树,对于 \(T\) 从根到下,依次删除点 \(u\) 得到的连通块在原图中是极大的连通块。
分析这个条件为什么难,“依次删除”是一个动态的过程,“极大”同时不好判定。我们希望将这个条件变得简单一点,更容易判定,同时仍然充分。
“极大”真的需要吗?是不是只要联通即可?并非如此。现在我们转换一下视角,删除点 \(u\) 得到极大连通块,所以我们考虑从每个点不断连接构成原树,此时是“只要联通即可”。对于点分树上点 \(u\) 到点 \(v\) 的连边,能构成这条连边的条件就是原树上 \(u\) 到 \(v\) 上所有点均在点分树中较深的子树内部(除了开头的那个点)。点分树钦定 \(1\) 为根,我们可以利用这个方法判定点分树上 $u,v $ 两点可能的父子关系。
反思一下这个流程,我们发现从点分树角度判定,不断“删除”太难了,然后我们就考虑:如果是加入一条点分树上的边呢?这个时候充要条件就产生了。这就是转换视角的魅力。
CF1810G The Maximum Prefix
容易想到一个暴力 dp,设 \(f[i, j, k]\) 为长度为 \(i\) 的序列,前缀和为 \(j\),最大前缀和为 \(k\)。直接转移,是 \(O(n^3)\) 的,拼尽全力无法战胜。
这过程中暗含了一个“模拟对数空间的图灵机”的过程,从左到右扫描一遍这个序列,求解最大前缀和必须要记录 \(max\) 和 \(summax\) 两个量,再算上阶段,此时无可避免的要涉及到 \(O(n^3)\) 的情况。
我们能够仅仅使用一个额外变量来计算最大前缀和吗?实际上是可以的!正着不行,考虑倒着,设上一轮的最大前缀和为 \(maxsum\),那么 \(maxsum\gets \max(0, a_i + maxsum)\)。如此就只用一个了!
接下来怎么 dp 呢?从后往前 dp 吗?这样我们就没办法对于每一个前缀求解了,仍然考虑从前往后 dp,利用提前钦定的思路,将后面部分的 \(maxsum\) 钦定为 \(j\),设 \(f[i, j]\) 为长度为 \(i\) 的前缀,后缀最值为 \(maxsum\) 的结果。提前钦定也是非常重要的 trick,下文会详细展开一下。
分部分解决
zxx 哥哥总是在题解中把一道题拆分成几个部分分开解决,我也应该学习伟大的 zxx 哥哥。
P8935 [JRKSJ R7] 茎
把问题分 part 解决,很显然除了 \(1\sim m\) 这条链上,外面的删除是相对自由的,可以直接用一个树上背包维护,而链上则需要单独 dp。前一个部分作为后一个部分求解的系数。这两部分几乎是完全独立的,所以可以分别求解。
做题的时候要有这样分开的意识!
寻找不变量
P7951 [✗✓OI R1] 右方之火(节选)
对于树的情况充要条件分析。
我都不会菊花图我能会这个吗(流泪。
找到一个度数大于等于 \(3\) 的节点为根,如果不存在说明是链,直接做。然后不断修改 \((x, fat_x, fat_{fat_x})\) 这个三元组,使得只剩下根这个菊花不为 \(0\),做菊花。
通常来说,对于一个看上去很对的构造方法,我们要相信它就是充分的。尝试证明。
首先分析一下这个构造方式的本质是什么。此时我们只在乎每个值 \(\bmod 2\) 后的结果。修改 \((x, fat_x, fat_{fat_x})\) 即给 \(x, fat_{fat_x}\) 异或上 \(s_x\)。观察不变量,发现树黑白染色后,黑点异或值和白点异或值总是不变。同时这个操作相当于使得根节点为黑点的所有异或值。有解当且仅当黑点异或值为 \(0\)。
证明如果黑点异或为 \(1\) 无解即可说明条件的必要性,通过构造即可说明条件的充分性。证明黑点若异或为 \(1\) 无解很简单,因为无论怎么修改集合异或为 \(1\) 说明一定有一个点无法被消为 \(0\),即无解。于是我们成功的证明这样构造合法是最终合法的充要条件。
操作次数只有根没有操作,是 \((n - 1)\) 次。
CF2143E Make Good
咕咕咕。
自由元相关
之前我喊它叫唯一确定的思想,认为是设一些元为常数后,整个盘面唯一确定。不过似乎有更深入的自由元相关的理论,那么就用这个文字吧。
有些问题发现可以采用一些自由元的想法,考虑只确定一部分,然后能不能唯一确定这个盘面?如果产生了不合法的情况,又应该怎样判断?
在线性代数部分的应用主要是分析一些高斯消元的时候,一定要注意秩的大小。
这个思想一个经典的应用就是统计异或和为某个定值的时候使用,接下来会介绍两道这样的题目。
P3214 [HNOI2011] 卡农
经典永流传,伟大无需多言。
形式化题意即给定 \(n\) 有 \(U = \{1, 2, 3, \dots, n\}\),询问有多少种方案选出 \(m\) 个互不相等的非空集合且最终异或和为 \(0\),集合内部有序。
首先只考虑异或为 \(0\),此时可以发现已知前 \((m - 1)\) 个集合就可以唯一确定最后一个集合了,最后一个集合是非自由元,于是这部分就是可以直接计算的。
现在都考虑起来。特别的因为我们每一个方案都考虑了 \(m\) 次最后要除以 \(m\)。
前面有 \(\binom{2^n - 1}{m - 1}\) 种。首先剔除最后一个是空集的情况,这部分有 \(f[m - 1]\) 个情况。然后是删除最后一个非空但是和之前某部分相等的情况,这部分是 \(f[m - 2](2^n + 1 - m)\),因为最后选的两个不能和之前重复。
于是可得 \(f[m] = \dfrac{\binom{2^n - 1}{m - 1} - f[m - 1] - (2^n + 1 - m)f[m - 2]}{m}\)。
P10104 [GDKOI2023 提高组] 异或图(节选)
沿用了类似的思想。
容斥部分很 trivial 不讨论。
随后回到了最沉重的话题,\(m = 0\) 怎么做?即:
有多少种 \(\{b\}\) 使得:
- \(\forall i, b_i \le a_i\)
- \(\oplus_{i = 1}^n b_i = C\)
观察:
- 这个问题明显不弱于最后异或和为 \(0\),所以我们可以转化成异或和为 \(0\),这样可以方便讨论。转化方式是:在最后增加一个 \(C\),减去最后增加一个 \((C - 1)\) 的方案数。问题变成了 \(C = 0\)。
- 如果没有 \(b_i \le a_i\) 的约束,或者说 \(a_i\) 都相等且为 \(2^k - 1\),那么这是很容易的,如果确定了前面的数,最后一个是唯一的。(P3214 [HNOI2011] 卡农)
直观感受上,首先这个不能集合幂级数,试试也可以知道得到的结果及其猎奇。其次为了维护 \(b_i \le a_i\) 的约束,只能考虑类似于数位 dp 的手法,从高往低做。
对于最高位 \(d\),如果有 \(k\) 个 \(1\),钦定好这一层的选择方式。只要选择将一个 \(1\) 修改为 \(0\),那么总数就可以直接计算。否则全部保留原位,我们需要用下一层的信息。所以可以考虑设 \(f[i, 0/1, 0/1]\) 代表考虑了前 \(i\) 个数,目前有没有将 \(1\) 修改为 \(0\),修改了的奇偶性下所有 \(a'_i + 1\) 的乘积。直接转移即可。
现在我们得到了一个 \(O(n\log V)\) 的做法。
通过唯一确定的方法,我们将问题变得更加自由,从而极度的简化了这题的做法。
从化简版本入手
这个问题真是太难了,于是我们考虑缩减一些条件,然后不断加强。
注意到很多题目我们已经自觉地再使用这个思想模式了。比如计数问题,多个约束那么就先从简单的约束考虑起;比如环问题,就从链问题考虑起;树问题就从菊花和链考虑起。
QOJ5374 数圈
神仙题!
很容易观察到一点,总和不变,因此无解情况应当是总和小于 \(0\)。如果总和恰好等于 \(0\) 但是最终不是全为 \(0\) 也无解,因为根本构造不出来最终情况前一步。
考虑如何刻画。首先环很难,考虑链,发现是交换前缀和。现在是环,可以考虑复制无限分,即构造无穷序列 \(a_i\),有 \(a_i = a_{i\bmod n}\)。链的话求解就是对 \(2\sim (n - 1)\) 部分排序,求逆序对。对于环的话,每次交换相邻两个位置会将所有模 \(n\) 同余下的位置交换,如何计算操作次数?
继续思考性质:因为是无限循环的序列,所以每个循环内一个点往后提供的逆序对数量 \(R_i = \sum\limits_{j > i}[S_j < S_i]\) 中满足 \(R_i = R_{i\bmod n}\)。每次交换 \(S\) 后对每个循环 \(R\) 产生的影响也是相同的。于是根据经典结论,最终答案也是 \(\sum\limits_{j = 0}^{n - 1}R_j\)。
\(R_j = \sum\limits_{i < j < i+n}\lfloor\dfrac{S_i - S_j - 1}{S_n}\rfloor + 1 = \sum\limits_{i < j < i + n}\lfloor\dfrac{S_i}{S_n}\rfloor - \lfloor\dfrac{S_j}{S_n}\rfloor + [S_i\bmod n > S_j \bmod n]\)
这是一个三维偏序模型。如果不考虑最后的艾弗森括号就是二维的,先做掉。考虑最后的艾弗森括号,由于存在 \(S_i \bmod n = S_{i + n}\bmod n\) 所以还可以拆成两个二维偏序。最终是 \(O(n\log n)\) 的。
总结:为什么我没有做出来这道题?
- 首先是我见到这道题我就被唬住了,我怎么可能会这么乱七八糟的操作。
- 接着是我对环问题的处理:我根本不会处理,根本没有去先考虑链再考虑环。虽然环有的时候存在利于操作的性质,但是更多时候还是不利于操作的性质。因此对于环上问题,最好先试试链的问题。
- 交换前缀和这样的模型明明很常见啊!P7962 这题是交换差分。
技术板块
接下来就是将分析方法和技术本身进行结合了。这是一些凌乱的东西。我们将用一级标题表示板块,二级标题表示板块下的计数,三级标题表示举例的题目。
最后的做题手段就是枚举所有可能的技术,挨个尝试。所以拥有一个完备的技术库是很有必要的。虽然我到现在还是没有 TAT。
主要集中记录我目前新学到的一些技巧。
计数
Min-Max 容斥
特别注意这里面 \(T\) 不能为空集,同时 \(T\) 可以等于 \(S\)。
证明:
以第一个式子为例,将 \(S\) 内元素从小到大排序,令 \(n = |S|\),对于第 \(i\) 个式子,后面有 \((n - i)\) 个元素,那么它所提供的贡献就是 \(\sum\limits_{j = 0}^{n - i}\binom{n - i}{j}(-1)^j = [n - i = 0]\),当且仅当 \(i = n\) 时会提供 \(n\) 的贡献,即 \(\max(S)\)。
由于期望具有线性性,所以这个东西可以直接套用到期望上面。
主要用来算期望,经典的运用在于:做集合覆盖,每一次有概率选择一个集合,询问期望覆盖完的时间,对于 \(E(\max(S))\) 转化成 \(E(\min (S))\) 就相对更容易直接计算。这个东西是比较经典的,主要用于没有思路了然后用来找突破口。kth 的拓展版本似乎用的不太多,这里一并写上。
计算局部贡献
特别和 dp 中常用的“均摊贡献”取了不一样的名字。在很多计数问题中,我们针对一步的贡献进行统计,可能会产生非常强大的效果。
QOJ3089. Harsh Comments
既可以用 min-max 容斥的模型,也可以:我们考虑一个人加进来以后产生的贡献。具体我没有研究
P10256 高仿的 Migos
听讲题还听到了另外的做法,对于边 \((i - 1, i)\),考虑它的贡献,对于每一个跨过这条边的的区间,考虑会不会回去,于是期望经过的步数是一个几何级数 \(\dfrac{1}{p_i}\)。现在考虑多个区间在一起,每个区间回不回去是一个独立事件,所以计算乘积就是这条边的贡献。
QOJ7509. 01tree
在杂项里面要记录这个相邻异或的 trick。
考察其中一条连边产生的贡献即可。虽然重点不在于此。
dp
延迟钦定
对于 dp 我们已知的内容是一个前缀,但是需要用到一个后缀的部分,此时我们钦定后缀部分的信息,然后来 dp。
- P8935
- CF1810G
- P8329
这些都是经典的题目。经典的应用比如:
- 对于 mex 问题,我们只记录最长前缀,在 mex 变化的时候再来统一变化时产生的贡献。
P7213 [JOISC 2020] 最古の遺跡 3
首先考虑充要条件。
显然我们不会选择从值域角度入手,太难了。考虑从下标角度,从后往前。我们可以得到这样一个结论:对于 \([i, n]\) 进行操作直到收敛,然后用这一部分再来和 \((i - 1)\) 操作,那么最终也可以得到正确的结果。证明从略,可以感性感知一下。令 \(h_i'\) 为 \(h_i\) 操作收敛后的结果。
因为我们只关心每一个点删空了没,那么删空的充要条件也就是 \([1, h_i]\) 在后缀 \([i + 1, n]\) 内均完全出现过。利用“延迟钦定”的思想,我们设 \(f[i , j]\) 为对于后缀 \([i, n]\) 内,极长连续前缀 \([1, j]\) 的结果。
如果 \(i\) 被删空了,那么 \(f[i + 1, j]\) 向 \(f[i, j]\) 贡献,令 \([i + 1, n]\) 内已经有 \(c_0\) 个被删空了,于是此时还可以选择的还剩 \((j - c_0)\),即 \((j - c_0)f[i + 1, j]\) 贡献到 \(f[i, j]\)。
如果 \(i\) 没有被删空,mex 不变,那么我们延迟计算;如果变化了,那么 \(f[i + 1, k]\) 向 \(f[i, j](k < j)\) 贡献。如果考虑操作后的,那么就是存在 \((j - k)\) 个还没有产生的数,\(h_i\) 最后变为 \((k + 1)\),然后把前面的串起来。对于前面的部分则构成了 \((k + 2)\sim j\)。对于 \(h_i' > j\) 的部分,我们不去管,需要的时候再来求解。我们发现我们根本不关心这两部分之间具体元素之间的相对位置,因为形成的 \(h\) 完全断开。
只考虑 \(h_i' \in [k + 2, j]\) 内的部分。令前面有 \(c_1\) 个没有被删空,其中有 \(k\) 个都是 \(1\sim k\) 内的,于是选择上这部分方案数就是 \(\binom{c_1 - k}{j - k - 1}\)。对于 \(i\) 位置,如果不考虑“每个元素最多被选两次”会有 \(2(k - j)\) 个选法,假设前面已经选完了 \((k - j - 1)\) 个那么此时只剩下 \((k - j + 1)\) 个了。注意到此时我们是将一个数出现的两次看作了不同的元素的,所以最后答案要除以 \(2^n\)。对于剩下部分的 \((j - k - 1)\) 个数,要求就是使用后可以完整的填充 \([j + 2, k]\) 这部分,我们不妨把值域投射到 \([1, j - k - 1]\) 上,此刻问题为:存在 \(m\) 对数,两个 \(1\) 到两个 \(m\),在其中选择一个大小为 \(m\) 的子序列使得用上面的方法操作后是 \(1\sim m\)。设为 \(g[m]\)。此时系数就是 \(\binom{c_1 - k}{j - k - 1}(j - k + 1)g[j - k - 1]\)。
考虑 \(g[n]\)。不难发现这是一个很类似的问题。将整个序列拆成两个部分,第一部分是一开始形成 \(1\) 开头的连续段,剩下的就是子问题。令第一部分大小为 \(i\),一共有 \(2i\) 个,前面放了 \((i - 1)\) 个最后剩下 \((i + 1)\) 个可选,然后前面部分连续是 \(g[i - 1]\),后面一部分连续是 \(g[n -i]\)。这两部分加入的相对顺序是 \(\binom{n - 1}{i - 1}\)(因为第一个固定),于是总转移为 $g_n = \sum\limits_{i = 1}^n g[i - 1]g[n - i]\binom{n - 1}{i - 1}(i + 1) $。
再次搞明白这个推导还是花了我很久,依旧没有完全把这道题掌握,到时候再看看吧。
我本人不太理解为什么我们把对于这类类 mex 问题和上面 P8935 内都叫做延迟钦定。对于类 mex 问题,核心思路在于“在改变的时候计算贡献”,而不是贡献均摊(即“费用提前计算”);对于 P8935 等题,关键在于“不知道后缀的信息”,于是我们考虑在状态中记录下后缀信息,这也是广义的费用提前计算。我觉得我们不能把它们混为一谈,但是似乎现在大家普遍是这么看的?那好吧。(瘫
集合幂级数
边双连通-联通变换
卧槽,彻底怒了,用户指出了最核心的矛盾点,我根本没有掌握什么是边双连通-联通变换。我必须承认我根本不会,现在完全不会集合幂级数的乃龙要把比赛给糊弄过去
给定一个图 \(G = (V, E)\),对于一个点集合 \(S\) 存在权 \(f_S\)(一般认为是只有联通块才能产生贡献)。对于 \(g_S\) 定义其权值为:将 \(S\) 进行子集划分为 \(T_1, T_2, \dots, T_m\),产生的权为 \(\prod\limits_{i = 1}^mf_{T_i}c^{m - 1}\) 再乘以将每个集合看作一个连通块,最后形成一张图的方案数,最后对每一个子集划分求和。
你可以把权值看作每一个联通快的贡献乘以每一条边产生 \(c\) 的贡献。我们记得到的集合幂级数 \(G = \operatorname{Trans}(F, c)\)。
称后来连边的边为树边。逐点牛顿迭代,每次考虑树边最大的点编号为 \(i\) 的情况,记录这一轮的集合幂级数系数为 \(g_{i, S}\)。如果 \(i\not\in S, g_{i, S} = g_{i - 1, S}\),这很平凡。对于 \(i\in S\),考虑 \(i\) 所在的联通快以及 \(i\) 向外的连边。令 \(i\) 所在联通快为 \(W(i \in W)\),接下来对于 \((S - W)\) 的子集划分为 \(T_1, T_2, \dots, T_k\),每一个子集内部都只能用小于 \(i\) 点编号的来向 \(i\) 连边,这一部分我们记作 \(\operatorname{coef}(T_j, i)\),局部内部是 \(f_{T_j}\),还有常数 \(c\) 的贡献,于是不难得到这样的式子:
定义 \(h_{i, S} = g_{i - 1, S}\operatorname{coef}(i, T_j)c\),于是就有 \(g_i = g_{i - 1}\times \exp (h)\)。
这样推导就叫边双连通-连通变换,时间复杂度 \(O(n^32^n)\)。
比较直观上的应用是子图生成树计数,虽然有更优秀的逐点牛迭做法,还有我都用这个了我为啥不直接 matrix-tree 也是一样的(
另外一种高妙运用:
特别的,我们考察一种这样的变换:\(F_S\) 初值为 \(S\) 内的连通图权之和,连通图的权定义为 \(c^{|E'|}\),\(|E'|\) 为这个连通图边数,随后进行 \(G = \operatorname{trans}(F, -c)\),则 \(G_S\) 为 \(S\) 内的边双子图的权之和,权定义仍然是 \(c^{|E'|}\)。
证明:考虑如何统计 \(S\) 内的贡献,对于导出子图 \(G'\) 计算权和,如果一条边是割边,那么可能在 \(F_S\) 中提供 \(c\) 的贡献,也可能为树边提供 \((-c)\) 的贡献;如果不是割边那么只能提供 \(c\) 的贡献。对于非割边,提供的贡献是一定的,于是我们只考虑前面这部分,若有 \(t\) 条割边,提供 \(\sum\limits_{i = 0}^{t}\binom{t}{i}((-c)^t c^{t - i}) = [t = 0]\) 的权,于是只有没有割边的子图,才会提供 \(c^{|E|}\) 的贡献。
更为一般的,这种变换可以看作是对 \(\operatorname{trans}(F, c)\) 的逆变换。从这个角度来看很多都可以迎刃而解了。举个例子,对于一般子图的 \(\operatorname{trans}(F, -c)\) 就是边双连通子图计数,因为边双连通子图用树组合出来就是 \(F\) 这个集合幂级数。
P11567 建造军营 II
对于 \(p_i, q_i\) 内部的所有边双全部染黑,除此之外所有边都是白色,一条黑边产生 \(1\),白边产生 \(2\)。
首先我们只考虑子图中黑边计数,最后要求产生的联通子图不能包含一堆 \((p, q)\) 有且仅有一个在这个图中,可以直接求出这样的集合幂级数 \(F\)。但是这样的黑子图可能不是极大的,即构成的边双中可能存在一个边双不存在任何任何 \((p, q)\)。于是 \(F' = \operatorname{trans}(F, -1)\),然后将不包含任何 \((p, q)\) 的 \(F'_S\) 赋为 \(0\)。
然后考虑白边,类似的操作也是求完 ln 后做 trans,不过 \(c = 2\)。也是因为如果存在 \(p, q\) 那么就不是极大白边所以把这部分去掉得到 \(G'\)。
然后答案的集合幂级数为 \(\operatorname{trans}(F + G', 2)\)。
数据结构
莫队或二区间合并
莫队和二区间合并都是解决静态序列问题的有力工具。
对于莫队而言是“单点加入局部”,对于二区间则是“单点加入局部”,接着是“局部和局部”的合并。不过无论如何,这都涉及到一个“两个方向”的操作。
对于一类序列询问问题不具有交换律,非常神秘,我们则可以从莫队入手考虑。(甚至可以直接从回滚莫队开始考虑),分析往前加入和往后加入的情况,然后试着改为二区间合并,优化为 log。
这个往往也是没有办法的办法,枚举可能的数据结构方法并且往里面带入。
UOJ749. 【UNR #6】稳健型选手
首先 \(q = 1, l = 1, r = n\) 的问题,尝试判定一组取法合法。
对于 \(\forall 2i \le n\),前缀中 A 至少取了 \(i\) 个,容易归纳法。此时则可以扫描一遍,考虑让先手取得尽可能少,于是维护一个小根堆,每次加入两个,再取出最小的那个。如此可以做到 \(O(n\log n)\)。特别的,做一些特判。首先是区间长度为奇数,此时最后一个一定只能由后手取到,所以下面认为所有区间长度为偶数。其次是这里面因为按照 \(l\) 奇偶性不同,每次加入的一对数不同,所以对 \(l\) 不同奇偶性分开来做。
这个没有优化的空间。对于第四档,我们可以考虑莫队。对于第五档,我们可以考虑猫树分治,但是这都离不开向两个方向的拓展。既然已经解决了固定 \(l\),\(r\) 可以增量计算。反过来也容易解决固定 \(r\),\(l\) 增量计算,类似的把小根堆换成大根堆倒过来做就好了。
可以获得一个 \(O(n\sqrt{q}\log n)\) 的回滚莫队做法,维护区间内部选中的大根堆和没有选中的小根堆,每次拓展两个可以直接用刚才的思路,维护一个小根堆,代表后手选中的;大根堆代表后手没有选中的。每次维护新加入的两个元素选中情况即可。
考虑猫树分治。对于 \([l, mid]\) 的后缀和 \([mid + 1, r]\) 的前缀,我们总是可以容易的维护出选中和未选中的部分。
考虑合并两个部分,此时就会产生本来一部分 \([l, mid]\) 内选中,现在选不上;本来 \([mid + 1, r]\) 内选不上,现在选中了。为什么不会出现 \([l, mid]\) 部分原来选不上现在选上了?因为这部分我们是用大根堆维护后缀的,如果原来的大根堆无法选中,新加上处理 \([mid + 1, r]\) 的大根堆肯定更无法选中,\([mid + 1, r]\) 同理。
令 \([l, mid]\) 中被替换的集合为 \(S\),\([mid + 1, r]\) 中被替换的集合为 \(T\)。分析性质:
- \(|S| = |T|\)
- 无论 \(S\) 和 \(T\) 位置如何,这个取法总是合法。因为根据充要条件,原本合法,现在更改后一定是前缀数量更多,后缀数量更少,所以不会破坏性质。
令 \(k = |S| = |T|\),可以想到只要维护 \([l, mid]\) 中选中的最小的 \(k\) 个和 \([mid + 1, r]\) 中没有被选中最大的 \(k\) 个即可,约束是 \(\operatorname{kthmin} S < \operatorname{kthmax} T\)。随着 \(k\) 变大,\(\operatorname{kthmin} S\) 变,\(\operatorname{kthmax} T\) 变大,为了使 \(\sum\limits_{x\in T} x - \sum\limits_{y\in S} y\) 尽可能大,我们二分最大的 \(k\) 即可。实现上可以直接使用主席树,最后是 \(O((n + q)\log ^2 n)\) 的。
二分图
最大匹配和 Hall 定理
Hall 定理:最大匹配为 \(|V_L| - \max\limits_{S\subseteq V_L}({|S| - |N(S)|})\)
特别的,对于任意一个集合 \(S\) 内使得 \((|T| - |N(T)|)\) 取得最大值并且 \(|T|\) 最小的 \(T\) 唯一,可以利用这个作为代表元。
Hall 定理题目太多了。
最大匹配和 Koing 定理
你现在遇到了最大匹配,但是 Hall 定理没有好用的出口,此时我们考虑 Koing 定理,即最大匹配等于最小点覆盖。这一个定理经常被遗忘掉。
12-29 模拟赛 T1
左右各有 \(n\) 个点二分图,每一个点拥有一个向量,每个点不同度数产生相应贡献。最多可以 \(m\) 条边,问当二分图最大匹配数量为 \(k\) 时的最大贡献,\(k\in [1, n], n \le m\)。
对于一个二分图,已知每个点度数,那么如何刻画可能的最大匹配数量?可能的最大值一定是左边度数非 \(0\) 点数量和右边的取 min,显然。但是可能的最小值呢?我们先抛开这个问题。如果我们知道最小值是 \(L\) 最大值是 \(R\),那么大胆猜测 \([L, R]\) 内都可以被更新到。
(我必须承认我并不会严谨的证明)
对于二分图最大匹配,除了 Hall 定理的刻画,还可以用 Koing 定理最大匹配数=最小点覆盖数!。为什么不是 Hall 定理。Hall 定理因为是“对于所有 \(S\) 求 \(N - \max\{|S| - |N(S)|, 0\}\)”,所以涉及到一一个对于集合 \(S\) 的枚举。通常来说,如果是完美匹配(\(|S|\le |N(S)|\) 是一个比上面求值容易的事情),或者是有一定性质(比如最优的 \(S\) 一定是后缀),那么选择 Hall 定理。但是由于这道题并没有任何有用的性质,而且考虑的“度数”是从“点”的角度出发,“点”覆盖“边”也很容易用度数刻画:如果 \(u\) 在所选点集合,那么和 \(u\) 的连边都被覆盖。优先选择对面没有被选中的点进行覆盖,于是可以得到左部选出来的度数 \(y1\) 和右边选出来的度数 \(y2\) 和要大于等于所选边数 \(j\),只要这样就可以得到一组合法的点匹配数量 \(x1+x2\)。
两个部分设 \(f[i, j, k, x, y]\) 为前 \(i\) 个点,总度数为 \(j\),有 \(k\) 个非 \(0\) 度数,有 \(x\) 点被选中作为点覆盖,总度数和为 \(y\),直接 dp 预处理,然后合并即可。复杂度 \(O(n^3m^3)\),我不知道为啥它能过,只能说卡常卡的。
完美匹配的奇偶性
完美匹配数量为这个邻接矩阵的积和式,所以没有 polyn 做法。但是它的奇偶性只和行列式有关,可以直接计算。
图论
优化建图
我已经若干次死在优化建图的手上了。
常见的优化建图手段,对于线性问题,可以考虑前缀优化建图,或者线段树优化建图(处理区间)。对于树上问题,可以考虑用类树上统计方式建图。即树上统计问题的特征在优化建图问题上也适用,毕竟一个道理。比如如果这个问题只和深度有关,即深度相同点等价考虑长剖。如果是距离相关,考虑用点分树来简化结构。特别的,可以考虑虚树如何应用。
树上常见的工具:长链剖分,此时要求深度相同的点等价;点分树,一般用于刻画距离相关的信息,比如压缩“两两距离”的连边。虚树,涉及特定点。dsu on tree 理论上是“在某一个维度上相同的点等价,且这个维度和子树大小同阶”可以用,不过我还没有看到过这样的题目。
GDKOI2025 T2
严肃拷打我的长剖水平。
考虑最短路优化建图,对于第三类点,因为和深度相关,对于一个点 \(u\) 在不同子树中同一深度的点会互相产生贡献,而且这部分点实际上是“等价”的,所以可以想到利用长剖来刻画,把它们放到一个虚点里面。具体在操作的时候,要把虚点拆成两个点,一个入点,一个出点。
首先是对于 \(u\) 合并不同子树内的点。对于一个深度 \(x\) 有很多虚点,我们已经拆成了入点和出点的形式。排成一排,然后对于一个点,它的出点向左边所有入点连边,它的入点被右边所有出点连边,边权为 \(c_d\)。这个部分可以利用前后缀建图优化。
然后是对不同子树之间的合并。全部合并到长链上面去。即将长链的入点向短链的入点连边,断链的出点向长链的出点连边。
首先这道题我做的时候怀疑有什么路径上面的性质,然后我就不会做了。但是这道题正解就是优化建图,其实直接优化建图我还是不会,小丑完了。
QOJ16120. Language Barrier
直接 dp 是 \(O(n^2)\) 的,考虑缩减状态数。观察:
- 如果 \(1\) 和 \(n\) 区间有交集,那么我们就直接从 \(1\) 走到 \(n\)。
- 无交集,钦定 \(1\) 在 \(n\) 左边,那么每次走一定都会走到右端点,直到有交。
- 行走过程中只有翻译点和终点有用。
即,行走过程中我们选择 \(1, p_2, p_3, \dots, p_m, n\),能够行走到的要求是两两有交,每次行走的代价是 \(\operatorname{dist}(x, y) + 1\),算上翻译代价,最后 \(-1\)。
为了优化建图,浓缩连边信息考虑点分树刻画所有路径。两两有交才能连边,利用线段树优化,这样做是 \(O(n\log^3 n)\) 的。但是注意到第二条性质,每次只会走右端点,所以我们只需要维护一个维度上的偏序关系就行了。时间复杂度就变成了 \(O(n\log^2 n)\)。
树上统计
树上统计常用的工具:
- 边分治,点分治。
- 通常来说,边分治是点分治的下位平替,因为还要进行一个树的三度化。
- 但是优势在于只将一棵树划分成了两个集合,在一些情况比点分治处理容易。
- dsu on tree
- 要求子树信息和子树大小同阶,即可做到 \(O(n\log n)\) 的总规模进行合并。
- 虚树。
- 对于部分点。
- 数据结构合并。
- 常用线段树,这个没什么好说的。
有一些题目需要用到边分治/点分治过后来建立虚树,在虚树上再进行分治或者别的手段维护。
详见 https://www.cnblogs.com/thirty-two/p/19524778 这篇文章,这里不再重复了。主要是提一嘴,过一下各种经典做法。
一些经典结论
直径相关:
- 对于一个点到它最远的点一定是直径的两个端点之一。
- 证明容易反证。
- 用一条边合并两个树,那么其直径一定是这 \(4\) 个点中选出来 \(2\) 个。
- 直径的中点重合。
- 这一点可以作为计数的基准。
重心相关:这个没什么好说的,不过一些和树的形态相关,划分联通快,并且希望联通快尽可能平均的题目可以试试用树的重心作为根,可能有奇效。比如 P9167 [省选联考 2023] 城市建造
杂项
有一些内容并不是很能归类到数学,数据结构,dp,图论之类的大项目,所以我们把它放到这里。
二进制计算器
不是很清楚这种东西学名叫什么。或者说有没有学名。
对于一类很奇妙和独特的二进制问题,考虑将二进制位先写出来,原本是对于每一行提供的贡献计算,现在我们矩阵转置,用 bitset(或者值域比较小就只用一个数)来维护每一位,从而快速计算贡献。这是一种有些像“计算竖式”的感觉。
这个做法也只能靠着“试用技术”来“试出来”,不太好规定这种方法能够做哪些问题。
P8569 [JRKSJ R6] 第七学区
从或换为与,减一下即可。暴力的做法是对于每一个数位维护它延申几长位 \(1\) 的后缀长度 \(l_j\)。直接做是 \(O(n\log V)\) 的更新。
现在我们来利用这个二进制计算器的思想,把 \(\log V\) 个 \(l_1\sim l_t\) 给列出来。其中将 \(a_i\) 所在的位的 \(l\) 全部加 \(1\),剩余位置全部清零。
完成清零很容易,直接与一下即可。对于 \(+1\),本质为将后缀极长的一段 \(0111\dots 11\) 取反。这部分发现也是可以用位运算直接维护的,就是维护一下当前还剩哪些位置要取反,然后再维护一下 01 位置即可。比较平凡。
P13497 【MX-X14-T7】墓碑密码
前面 fwt 分析部分十分 trivial,都做过一万遍了,直接跳过。
设 \(cs_T = \sum\limits_{W \in S} [|W \cap T| \bmod 2 = 0]\) 要求 \(0\sim V\) 范围上的全部取值。
考虑增量计算,充分利用 \(cs_{i -1}\) 到 \(cs_i\)。注意到对于 \((i - 1)\) 到 \(i\) 一定是将最后一个后缀全部修改为 \(0\),然后最后一个 \(0\) 修改为 \(1\),即取反一个后缀。
那么产生的变化量也就在这里面。维护一个 \(n\) 行,\(L\) 列的矩阵,每一行都是 \(W\in S\)。提前维护出来对于后缀上若干个列,从和 \(011\dots 1\) 到和 \(100\dots00\) 与出来的差即可求出哪些的 \(|W \in i|\) 奇偶性产生了变化。令原本 \(|W\in i|\bmod 2 = 0\) 部分有一个 bitset,然后修改后将这个 bitset 与上变化位置即可。这部分是 \(O(\dfrac{|V||S|}{w})\),谁能想到 \(\dfrac{|S|}{w} = 2\),真是令人忍俊不禁。
考虑 01 序列
这个已经经典的不能再经典了,但是有的时候还是会忘掉。
题目太多了不放了。

浙公网安备 33010602011771号