2024 秋冬做题记录
题单
字符串专区
无特殊说明就是字符集为小写字母。
CF587F Duff is Mad
给定 \(n\) 个字符串 \(s_{1 \dots n}\)。
\(q\) 次询问 \(s_{l \dots r}\) 在 \(s_k\) 中出现次数之和。
\(n,q,\sum_{i=1}^n |s_i| \le 10^5\)。
建 AC 自动机,计算 \(s_i\) 在 \(s_j\) 中出现次数:
-
将 \(s_j\) 对应的 AC 自动机节点权值 \(+1\)。
-
\(s_i\) 在 \(s_j\) 中出现次数相当于 \(s_i\) 的结尾对应的节点 \(endp_i\) 的 fail 树子树权值和。
根号分治 \(|s_k|\),设置阈值 \(B\):
- \(|s_k|>B\),将 \(s_k\) 对应的 AC 自动机节点权值 \(+1\),处理出 \(f_i\) 为 \(endp_i\) 的 fail 树子树权值和,处理前缀和查询 \(\sum\limits_{i=l}^r f_i\)。
- \(|s_k|\le B\),我们转换思路,将 \(endp_i\) 的 fail 树子树加,查询 \(s_k\) 对应的 AC 自动机节点权值和,拍扁 dfn 用 BIT 维护,扫描线处理询问。
复杂度分析默认 \(L=\sum|s_i|\) 与 \(n\) 同阶。
第一部分复杂度 \(O(\dfrac{n^2}{B})\),第二部分复杂度 \(O(nB\log n)\),取 \(B=\sqrt{n\log n}\),复杂度 \(O(n\sqrt{n\log n})\)。
但你发现这实在太蠢了,第二部分 \(O(n)\) 次区间加,\(O(nB)\) 次区间查,改用分块平衡到 \(O(n\sqrt n+nB)\)。
这时候就可以取 \(B=\sqrt n\),时间复杂度 \(O(n\sqrt n)\)。
upd:之前那发分块写错了,但是
inline void mdf(int l,int r){
for(int i=l;i<=r;i++)
{
val[i]++;
}
return ;
}
(忘记删暴力了)直接就过了???CF 数据强度呢?
但是分块比暴力跑得慢……
CF1483F Exam
给定互不相同的字符串 \(s_1, s_2, \cdots, s_n\),求有多少对 \((i, j)\) 满足:
- \(i \neq j\)
- \(s_j\) 是 \(s_i\) 的子串。
- 不存在 \(k\) \((k \neq i, k \neq j)\) 满足 \(s_j\) 是 \(s_k\) 的子串且 \(s_k\) 是 \(s_i\) 的子串。
\(n,\sum \lvert s_i \rvert \leq 10^6\)。
建 AC 自动机,枚举串 \(s_i\),统计 \(s_j\) 的数量。
对于 \(s_i\) 的某个前缀 \(s_i[1\dots p]\),它的后缀能匹配很多个 \(s_j\),然后只有这些 \(s_j\) 种最长的才可能合法。
考虑如何找到 \(s_i[1\dots p]\) 的后缀能匹配的最长 \(s_j\)。设 \(s_i[1\dots p]\) 在 fail 树上的节点是 \(x\),\(s_i[1\dots p]\) 能匹配 \(s_j\) 当且仅当 \(endp_j\)(\(s_j\) 的结尾对应的节点)是 \(x\) 的祖先(fail 树上)。
那么最长的 \(s_j\) 即为最深的 \(endp_j\),于是可以 dfs 一遍预处理 fail 树上每个节点的最深 \(endp\) 祖先。
这里有一个细节,就是你 \(s_i\) 整个串(长度为 \(|s_i|\) 的前缀)选出来的 \(j=i\),应该为 fail 树上 \(endp_i\) 的不是它本身的最深 \(endp\) 祖先。
但是这样求出的 \(j\) 不一定满足第三条限制。可能 \(s_j\) 是 \(s_i[1\dots p]\) 的的最长匹配后缀,但存在 \(s_k\) 是 \(s_i[1\dots q]\) 的的最长匹配后缀并且 \(q-|s_k|+1\le p-|s_j|+1\),此时 \(s_k\) 包含 \(s_j\)。
这个其实比较好解决,把 \(p\) 从大到小扫,记录当前最左的 \(p-|s_j|+1\)。
好了这样我们就能统计出每个 \(s_j\) 在 \(s_i\) 中,不被其他 \(s_k\) 覆盖地出现的次数了。
只需要判定这个次数是否等于 \(s_j\) 在 \(s_i\) 中出现的总次数即可,这个在 fail 树上单点加子树查,拍扁 dfn 用 BIT 维护。
\(L=\sum|s_i|\),复杂度 \(O(L\log L)\)。
P8571 [JRKSJ R6] Dedicatus545
给定 \(n\) 个字符串 \(s_{1 \dots n}\)。
\(q\) 次询问 \(s_{l \dots r}\) 在 \(s_k\) 中出现次数的最大值。
\(1\le n,q \le 10^5\),\(1\le\sum_{i=1}^n|s_i|\le 5\times10^5\)。
是不是很熟悉,CF587F Duff is Mad 求和变最大值。
再次考虑根号分治:
\(|s_k|>B\),将 \(s_k\) 对应的 AC 自动机节点权值 \(+1\),处理出 \(f_i\) 为 \(endp_i\) 的 fail 树子树权值和,线段树维护 \(f_i\),查询 \(\max\limits_{i=l}^r f_i\)。
\(|s_k|\le B\),将 \(s_k\) 对应的 AC 自动机节点权值 \(+1\),查询 \(s_i\) 在 \(s_k\) 中的出现次数时为 \(endp_i\) 的子树和。
给 \(s_k\) 对应的 AC 自动机节点在 fail 树上建虚树,\(endp_i\) 的子树和必然等于虚树上某一节点的子树和。
考虑查询 \(l,r\) 时枚举这个虚树上的节点 \(x\),它作为答案当且仅当 \(x\) 到根的路径上存在某个结尾节点 \(endp_i\)(\(i\in[l,r]\))。
于是我们扫描线 \(r\),维护 fail 树上 \(x\) 到根的路径的 max,如果这个值大于 \(l\),则 \(x\) 可以贡献答案。
拍扁 dfn 维护,区间取 max(其实修改单调,相当于区间推平),单点查询。采用 \(O(\sqrt L)-O(1)\) 分块。
其实在虚树时并不需要建虚树,只需要知道虚树有哪些点,BIT 维护子树和即可。
取 \(B=\sqrt L\),时间复杂度 \(O(L\sqrt L)\),时限 \(1s\),卡常,细节较多。
P5840 [COCI2015] Divljak
有 \(n\) 个字符串 \(s_1,s_2,\dots,s_n\),有一个字符串集合 \(T\),初始为空。
有以下两种操作:
- 往 \(T\) 中加入字符串 \(t\)。
- 查询 \(T\) 中有多少个字符串包含 \(s_x\)。
\(1\le n,q\le 10^5\),\(\sum|s|,\sum|t|\le 2\times10^6\)。
\(s\) 建 AC 自动机,增加文本串 \(t\) 时 \(s_i\) 的答案增加:
\(t\) 的所有 AC 自动机节点在 \(endp_i\) 的 fail 树子树的出现次数。
建个虚树,搞完了,但其实可以隐式建树,按照 dfn 排序 BIT 维护就行。
具体地,将 \(t\) 对应 AC 自动机节点按照 dfn 排序为 \(p\),\(p_i\to rt\) 链 \(+1\),\(\operatorname{lca}(p_{i-1},p_i)\to rt\) 链 \(-1\)。
链加单点查 \(\to\) 单点加子树查,拍扁 dfn 用 BIT 维护。
复杂度 \(O(L|\Sigma|+L\log L)\),我写了 ST 表 lca 所以空间复杂度 \(O(L\log L)\)。
图论专区
P6914 [ICPC2015 WF] Tours
给定一张 \(n\) 个点 \(m\) 条边的无向图,你需要选择一个颜色种类数 \(k\),然后用这 \(k\) 种颜色给每条边染色,要求对于图中任意一个简单环,每种颜色的边的数量都相同。求所有可行的 \(k\)。
保证图无重边,无自环。
\(1\le n,m\le 2\times10^3\)。
考虑将简单环划分成若干个不交的环,假如有两个环 \(X,Y\) 有交,那么将它们划分成 \(X-X\cap Y,Y-X\cap Y,X\cap Y\),这样一定可以划分出来。
然后有一个非常牛的结论(我不会证明:
- 两条边最终属于同一个环当且仅当:对于某一个环,它们要么都在环内,要么都在环外。
trick:划分后无交的简单环删去一条边,剩下的边为割边。
于是枚举一条非割边,计算出删除这条边之后新增的割边数量就能求出他所属简单环长了。
答案是所有简单环长的 gcd 的所有因数。
时间复杂度 \(O(m^2)\)。这题还有神仙哈希做法 \(O(n+m)\)。/jk
P10873 [COTS 2022] 帽子 Šeširi
有 \(n\) 个人,每人有一个黑色或白色的帽子,每个人只能看见别人的帽子。
给每人(看到的 \(2^{n-1}\) 种状态)制定一种策略,使得戴白帽子的至少有一半(向下取整)猜对自己帽子颜色,戴黑帽子的也至少有一半猜对自己帽子颜色。
\(4\le n\le 18\)。
对于某一个人看见了 \(S\) 的状态时猜对了:
- 他是白帽子,整个序列为 \(S_0=S+\{0\}\)。
- 他是黑帽子,整个序列为 \(S_1=S+\{1\}\)。
我们建一张二分图,给左部 \(S_0\),右部 \(S_1\) 连边。
给这个二分图定向,对于最终定向的某条边 \(X\to Y\),代表状态为 \(X\) 会猜对一个人,状态为 \(Y\) 会猜错一个人。
那么我们希望每个点的出边至少是它的度数的一半,这是欧拉回路的形式。
但是还有奇点,因为向下取整有 \(1\) 的容错,建虚点连接所有奇点跑欧拉回路即可。
时空复杂度 \(O(n 2^n)\),实现差的话时间复杂度 \(O(n^2 2^n)\)。
CF1215F Radio Stations
有 \(n\) 个区间 \([l_i,r_i]\),在 \(1\sim m\) 中选择一个点 \(f\)。
假如 \(f\in[l_i,r_i]\) 则可以选择 \(i\)。
另有 \(a\) 个限制形如:\(i\) 和 \(j\) 至少要选一个。
另有 \(b\) 个限制形如:\(i\) 和 \(j\) 至多可以选一个。
构造 \(f\) 和选择方案。
\(2\le n,m,a,b\le 4\times 10^5\)。
后两个限制明显是 2-SAT 板子。
\(T_i\) 表示选 \(i\),\(F_i\) 表示不选 \(i\)。
\(a\) 限制:\(F_i\to T_j\),\(F_j\to T_i\)。
\(b\) 限制:\(T_i\to F_j\),\(T_j\to F_i\)。
考虑 \(f\) 的限制怎么做,可以大力线段树优化建图(丑陋)。
trick:前缀和优化建图。
\(P_i\) 表示 \([f\le i]\),\(Q_i\) 表示 \([f>i]\)。
\(P_i\to P_{i+1}\),\(Q_i\to Q_{i-1}\)。
对于 \(f\in [l_i,r_i]\) 可以表示为:
- \(T_i\to Q_{l_i-1}\),\(T_i\to P_{r_i}\)
- \(P_{l_i-1}\to F_i\),\(Q_{r_i}\to F_i\)
跑 2-SAT 即可。
[ARC153F] Tri-Colored Paths
有一张 \(n\) 点 \(m\) 边的简单无向连通图。
你需要给每条边染上三种颜色之一,使得图中存在一条路径,经过三种不同颜色的边。
求合法染色方案数,对 \(998244353\) 取模。
\(3\le n,m\le 2\times 10^5\)。
显然正面考虑特别难,考虑反面情况。
有两种或仅一种颜色的情况是 trival 的,不做赘述。
计算恰好三种颜色并且图不合法的方案数。
下面称这三种颜色为红、黄、蓝。
\(\rule{20cm}{0.05em}\)
先考虑一个弱化问题:图是一棵树。
必存在点 \(x\) 使得连 \(x\) 的边有至少两种颜色。
不妨设 \(x\) 连了红、蓝边。把 \(x\) 当作树的根。
发现 \(x\) 连了红、蓝边的子树内不能有黄边,否则会出现合法路径。
于是黄边只能全部在 \(x\) 连黄边的子树内。
结论:存在点 \(x\) 使得 \(x\) 连出三种颜色的边,且 \(x\) 同一子树内的边和 \(x\) 连向这个子树的边颜色相同。
方案数就很好统计了,枚举一个点作为 \(x\),设度数为 \(d\):
容斥一下,方案数 \(f(d)=3^d-3\cdot 2^d+3\)。
总方案数就是 \(\small{\displaystyle\sum_x f(deg_x)}\)。
\(\rule{20cm}{0.05em}\)
接下来考虑图的问题。
先考虑环,环长 \(>3\) 则不可能环上有三种颜色。
特殊情况:三色三元环。
三元环仅一个点向外连边,一定是一个点双,方案数为 \(6\)。
三元环上有两个点向环外同一个点连边。
还有特殊情况:
发现此时每个点都不能再连任何颜色的边。
把 \(n=4\) 的这种情况(仅一个点双,\(m\ge 5\))特判即可。
接下来认为环最多两种颜色。
若环上有两种颜色,不妨设为红、蓝。环外必定有一条黄边,由于图连通,这条黄边肯定能走到环上。
环上必有红或蓝边大于 \(2\) 条,则必定有合法路径。
结论:简单环上边的颜色相同。
扩展结论:点双内边的颜色相同。
于是建圆方树,问题变为:将方点染色,使得不存在路径经过三种不同颜色的方点。
用树的结论:必然存在圆点 \(x\) 连出三种颜色的边,且 \(x\) 子树内方点颜色与子树的根相同。
答案即为 \(\small{\displaystyle\sum_x f(deg_x)}\),\(x\) 是圆点。
时间复杂度 \(O(n+m)\)。
二分图分区
[AGC029F] Construction of a tree
有 \(n\) 个点,给定 \(n-1\) 个点集 \(S_1,S_2,\dots,S_{n-1}\),\(|S_i|\ge 2\)。
从每个 \(S_i\) 中选出两个不同的数 \(a_i,b_i\),连接 \(a,b\) 两个点。
构造方案使最后连成一颗树。
\(1\le n\le10^5\),\(1\le\sum|S_i|\le 2\times10^5\),时限 \(4s\)。
令每个集合选出的 \(a_i,b_i\) 最后在树上 \(a_i\) 是 \(b_i\) 的父亲。
假设树根为 \(rt\),那么 \(b_i\) 互不相同,且 \(\{b_i\}=\{x|x\ne rt\}\),意思是,除了 \(rt\) 每个点都有一条到其父亲的边。
那么我们得出一个必要条件:从每个集合中选出 \(1\) 个数,可以选出 \(n-1\) 个互不相同的数。
我一看,这不是二分图吗,左侧点表示选出的数,右侧点表示点集编号,最大匹配要等于 \(n-1\)。
找到最大匹配(不等于 \(n-1\) 就无解)可以确定 \(rt\) 和 \(b\),我们考虑这样一个 bfs 构造方法:
当前点为 \(x\)(初始为 \(rt\)),找到 \(x\) 所属的点集 \(y\),若 \(b_y\ne x\) 且未确定 \(a_y\),则 \(a_y=x\) 并将 \(b_y\) 加入 bfs 队列。
假设没有这样 bfs 没能构造完,构造出的 \(a_i\) 的集合为 \(T\),注意到 \(x\) 属于的点集 \(y\) 相当于二分图上 \(x\to y\),那么二分图左侧为 \(T\) 不符合 Hall 定理,矛盾,所以肯定可以构造出来。
还有一种构造不出的情况为可以把 \(n-1\) 个点集划分为两份,两份的点集两两不交,bfs 判断是否构造完即可。
用 dinic 跑二分图最大匹配,时间复杂度 \(O(N\sqrt N)\)(\(N=\sum|S_i|\))。
[AGC067A] Big Clique Everywhere
给你一个 \(n\) 个点 \(m\) 条边的简单无向图 \(G\),你需要判定 \(G\) 中是否所有点导出子图都存在点数不少于一半的团。
多组数据,\(1\le T\le 10^3\),\(1\le\sum n\le 10^5\),\(0\le\sum m\le 10^6\)。
trick:原图中的一个团是补图中的独立集。
trick:只有二分图不存在导出子图最大独立集 \(<\dfrac{|V|}{2}\)。
证明:奇环最大独立集 \(<\dfrac{|V|}{2}\)。
于是变为判定 \(\neg G\) 为二分图。
注意到 \(\neg G\) 边数为 \(\dfrac{n(n-1)}{2}-m\),二分图至多 \(\lfloor\dfrac{n^2}{4}\rfloor\) 条边,故 \(\dfrac{n(n-1)}{2}-m>\lfloor\dfrac{n^2}{4}\rfloor\) 不合法。
判掉以后 \(m\) 是 \(O(n^2)\) 级别的,直接 \(O(n^2)\) 求出补图判断二分图即可。
时间复杂度 \(O(m)\)。
P9726 [EC Final 2022] Magic
序列 \(a_{0\dots 2n}\) 初始全为 \(0\),有 \(n\) 次覆盖操作 \(l_i,r_i\) 表示把 \([l_i,r_i)\) 区间覆盖为 \(i\)。
可以任意排列操作顺序,最大化 \(\sum\limits_{i=0}^{2n-1}[a_i\ne a_{i+1}]\),求这个最大值。
保证 \(l_i,r_i\) 互不相同。
\(n\le 5\times10^3\)。时限 \(3s\),空限 \(16M\)。
若位置 \(x\) 最终满足 \(a_{x}\ne a_{x+1}\)。
找到操作 \(p\) 满足 \(l_p=x+1\) 或 \(r_p=x+1\)。只有 \(p\) 操作后才能 \(a_{x}\ne a_{x+1}\)。
还要满足之后不会有操作把 \(x,x+1\) 都覆盖了,也就是 \(p\) 之后不存在操作 \(q\) 满足 \(l_q\le i<r_q-1\)。
于是可以考虑操作 \(p,q\) 的顺序对答案的影响:
不妨设 \(l_p<l_q\)
-
$[l_p,r_p)\cap[l_q,r_q)=\emptyset $,无交,不影响。
-
\([l_p,r_p)\subseteq [l_q,r_q)\),包含,一定是先操作 \(p\)。
-
\(l_q<r_p\),有交但不包含,先操作 \(p\) 则 \(a_{l_q}=a_{l_q+1}\),先操作 \(q\) 则 \(a_{r_p}=a_{r_p+1}\)。
第三种情况可以描述为:\(l_q\) 和 \(r_p\) 只能选一个。
按照 \(l_q\longleftrightarrow r_p\) 的方式建图后答案为最大独立集。
\(l_i,r_i\) 互不相同,这个图为二分图。
trick:\(\small\mathbf{二分图最大独立集大小}=\small\mathbf{点数}-\small\mathbf{二分图最大匹配}\)。
空限太小,bitset 邻接矩阵跑 dinic,时间复杂度 \(O(\frac{n^3}{\omega})\),但是 dinic 常数你不用担心。
树上问题专区
P8990 [北大集训 2021] 小明的树
给一棵 \(n\) 个点的树(根为 \(1\))和一个长为 \(n-1\) 的操作序列 \(a\),\(a\) 为 \(2\sim n\) 的排列。
定义这棵树的权值为:
初始所有点为黑色,依次把 \(a_1\dots a_{n-1}\) 染为白色,每次染色后若树是“美丽”的,则树的权值加上当前白点连通块数量。
树是“美丽”的当且仅当每个白点的子树内都是白点。
给 \(m\) 次修改:断掉 \((x1,y1)\) 的边并将 \((x2,y2)\) 连边,保证修改后还是一棵树。
求出初始和 \(m\) 次修改后的树的权值。
\(1\le n,m\le 5\times10^5\),时限 \(3s\)。
观察性质:
树是“美丽”的当且仅当每个白点的子树内都是白点,这个太难维护了,我们从黑点的角度考虑。
我们将其转化为:每个白点的子树内都没有黑点,也就是每个黑点到根的路径上都没有白点。
注意到根是黑点,所以这个条件相当于:黑点形成一个连通块。
trick:每个白点的子树内都是白点 相当于 黑点形成一个连通块。
然后点边容斥:
trick:黑点连通块数量 = 黑点数量 - 两端都是黑点的边的数量。
时刻 \(i\) 的黑点数量为 \(n-i\),定义 \(t_x\) 为 \(x\) 被操作的时刻(\(t_{a_x}=x\)),对于每条边 \((u,v)\),时刻 \([1,\min(t_u,t_v))\) 内它两端都是黑点,黑点连通块个数 \(-1\)。
这样就能用线段树维护 \(f_i\) 表示时刻 \(i\) 的黑点连通块个数。
即 \(f_i=n-i\),对于每条边 \((u,v)\),\(\forall i\in[1,\min(t_u,t_v)),f_i\gets f_i-1\)。
然后还有一个问题:白点连通块数量怎么求?
注意到每个白点连通块必为原树一整棵子树,则 白点连通块数量 = 两端是黑点、白点的边的数量。
同样的,对于每条边 \((u,v)\),时刻 \([\min(t_u,t_v),\max(t_u,t_v))\) 内它两端是黑点、白点,白点连通块个数 \(+1\)。
这样就也能用线段树维护 \(g_i\) 表示时刻 \(i\) 的白点连通块个数。
即对于每条边 \((u,v)\),\(\forall i\in[\min(t_u,t_v),\max(t_u,t_v)),g_i\gets g_i+1\)。
现在我们考虑如何查询,答案即为 \(\sum\limits_{f_i=1}g_i\)。
注意到 \(f_i\ge 1\),兔队线段树维护 \(f\) 的区间最小值,区间最小值个数,区间 \(f\) 最小的位置的 \(g\) 的和。
修改对边 \((x1,y1)\) 和 \((x2,y2)\) 贡献重新算即可。
时间复杂度 \(O((n+q)\log n)\),读入量略大。
[ARC181E] Min and Max at the edge
给一个 \(n\) 点 \(m\) 边的图 \(G\),定义生成树是“好的”满足:
对于非树边 \((u,v)\)(\(u<v\)),\(u=\min\limits_{x\in path(u,v)}x\),\(v=\max\limits_{x\in path(u,v)}x\)。
也就是生成树上 \(u,v\) 的简单路径的编号最小、最大点分别是 \(u,v\)。
对于每条边,求:删去这条边后的图 \(G'\) 是否存在“好的”生成树。询问相互独立。
\(1\le n\le 2\times10^5\),\(n-1\le m \le 2\times10^5\)。
考虑非树边 \((u,v)\) 的限制,看着很像 kruscal 的证明:非树边权值大于等于所有路径上的树边的权值。
但是这题是点权,考虑转化为边权。
需要构造一种算边权方式使得 \(w(u,v)\) 大于所有树边 \((x,y)\) 权值。
题目条件有 \(u\le x\le y\le v\),\(w\) 需要满足 \(\forall u\le x\le y\le v,w(x,y)<w(u,v)\)。
令 \(w(u,v)\) 为二元组权值 \((-u,v)\),权值双关键字比较,这样满足条件。
可以将双关键字压缩,变为 \(w(u,v)=(n-u)\times(n+1)+v\)。
于是“好的”生成树必须是按 \(w\) 生成边权的最小生成树。
这个最小生成树的非树边 \((u,v)\) 必定满足 \(u=\min\limits_{x\in path(u,v)}x\),但是不一定满足 \(v\) 的限制。
倒着做一遍,\(w(u,v)\) 为二元组权值 \((v,-u)\),双关键字压缩 \(w(u,v)=v\times(n+1)-u\),这样的最小生成树满足 \(v\) 的限制。
我们注意到 \(w\) 互不相同,所以最小生成树唯一,判断这两棵生成树是否相同即可。
判断树是否完全相同可以集合哈希,现在问题来了:删除某一条边后的最小生成树如何做?
先求出原来的最小生成树。假设删去了边 \((u,v)\),在原来的最小生成树上 \(u\) 是 \(v\) 的父亲,那我们相当于断开了 \(v\) 整棵子树,变成两个连通块。
考虑 kruscal 的过程,我们需要在剩下的边中选择一条最大的边 \((x,y)\) 连接两个连通块。
那么我们需要求出除去 \((u,v)\) 最小的边满足 \(x\) 在 \(v\) 子树外,\(y\) 在 \(v\) 子树外。
拍扁 dfn 序,令 \([l,r]\) 表示 \(v\) 的子树 dfn 区间,\(x,y\) 代指 \(dfn_x,dfn_y\)。
那么 \(x\notin[l,r],y\in[l,r],(x,y)\ne(dfn_u,l)\)。
拆为两部分:
- \(x\in[1,l-1],y\in[l,r]\)
- \(x\in[r+1,n],y\in[l,r]\)
只计算不在原来的最小生成树上的 \((x,y)\),则不会算到 \((u,v)\)。
分别矩形求最大值即可,发现 \(x\) 必为前后缀,扫描线+线段树维护即可。
时间复杂度 \(O(m\log m+m\log n)\),空间复杂度 \(O(n)\)。
样例的最小生成树:
DP 专区
P9040 [PA2021] Desant 2
有序列 \(a_{1\dots n}\),给定长度 \(k\),\(q\) 此询问:
询问区间 \([l,r]\),从 \(a_{l\dots r}\) 中选出若干个不相交的长度为 \(k\) 的区间,求选出的数的和的最大值。
\(1\le n,q\le 10^5\),\(1\le k\le n\),\(|a_i|\le 10^9\)。
不知道为什么时限 \(10s\),空限 \(1G\),PA 也太大方了吧,正解完全可以 \(1s\) 的。
PA 原题 时限 \(42s\),这不是直接 \(O(qn)\) 冲过?
回归正题,有显然 \(O(qn)\) DP:
记 \(s_i=\sum\limits_{j=1}^i a_j\),询问区间 \([l,r]\),设 \(dp_i\) 为 \(l\sim i\) 的答案。
将序列每 \(k\) 个分块,画出 DP 转移的 DAG:(一个 \(n=12,k=3\) 的例子,蓝色框住的为一块。)
问题形如 \(l\to r\) 的最长路形式。
记块数 \(m=\lceil\dfrac{n}{k}\rceil\),我们对这 \(m\) 个块分治。
分治区间 \([L,R]\),中点 \(M\),计算左端点在 \([L,M]\) 块内,右端点在 \([M+1,R]\) 块内的询问。
注意到最长路一定每个块经过一遍,故一定经过第 \(M\) 块的某个点,称这个点为“断点”。
处理出分治区间每个点到第 \(M\) 块的所有点的最长路,询问 \(O(k)\) 枚举“断点”。
时间复杂度 \(O(n\log n+qk)\),那这整了半天不还是一样吗。
观察到这个分治其实是对 \(m\times k\) 的矩形的列的分治。
trick:对面积为 \(S\) 的矩形分治,选择行与列中较大维分治,较小维长度 \(O(\sqrt{S})\)。
那我们能对列分治,为什么不能对行分治呢。
但由于这些红色边的存在,所以不满足必须每个块经过一次的性质。
只有第一次对行分治是会考虑红色边,需要特殊处理。
把上述性质该为:一定经过第 \(M\) 块的某个点或者某条红色边。
我们将红色边也视为一个“断点”,处理出分治区间每个点到红色边端点的最长路。
由于红色边数量和列数同阶,所以复杂度不变。
设当前分治矩形面积为 \(S\),时间复杂度 \(T(S)=2T(\dfrac{S}{2})+O(S\sqrt S)=O(S\sqrt S)\)
总共时间复杂度 \(O((n+q)\sqrt n)\)。
贪心专区
CF335F Buy One, Get One Free
有 \(n\) 个馅饼,第 \(i\) 个价格为 \(a_i\)。
购买一个价格 \(v\) 的馅饼可以免费兑换一个价格 \(<v\) 的馅饼。
求买完这 \(n\) 个馅饼的最小代价。
\(1\le n\le 5\times10^5\),\(1\le a_i\le 10^9\)。
这种东西,除了 DP 就是反悔贪心。
DP 看起来(?)没前途,考虑反悔贪心。
先合并价值相同的馅饼,按照价值从大到小排序。
现在的馅饼价格为 \(x\),有 \(c\) 个。
之前一共获得了 \(s\) 个馅饼,其中有 \(t\) 个是兑换的。
当前有 \(s-2t\) 个购买的馅饼还没有兑换。
先贪心地全部兑换,兑换 \(\min(s-2t,c)\) 个。
剩下的 \(r=c-\min(s-2t,c)\) 个需要购买或反悔。
用一个小根堆 \(P\) 维护兑换的馅饼。
注意 \(P\) 维护的是兑换的馅饼,也就是说兑换的价值和为 \(P\) 价值和,兑换的个数就是 \(|P|\)。
假设之前兑换了一个价格为 \(y\) 的馅饼(\(P\) 的堆顶):
-
\(x>y\)
-
\(r>1\)
兑换 \(y\) 改为买 \(y\),多出来 \(2\) 个兑换名额,用于兑换 \(2\) 个 \(x\),肯定更优。
\(P\) 的操作:加入 \(2\) 个 \(x\)。
-
\(r=1\)
兑换 \(y\) 改为买 \(y\),多出来 \(2\) 个兑换名额,用于兑换 \(1\) 个 \(x\),还剩 \(1\) 个名额以后用。
\(P\) 的操作:加入 \(x\)。
-
-
\(x\le y\)
-
\(r>1\)
- 保留 \(y\),买 \(x\),相当于不改变兑换决策。
- 购买 \(y\),多出来 \(2\) 个兑换名额,兑换 \(2\) 个 \(x\)。
- 购买 \(y\),用其他名额兑换 \(x\)。
我们抽象出一个价格为 \(2x-y\) 的馅饼,和原本价格为 \(y\) 的馅饼加入堆。
如果 \(2x-y<0\) 说明买 \(2\) 个 \(x\) 一定更优,不能加入 \(2x-y\) 的馅饼。
由堆内是否剩下 \(y\)、\(2x-y\) 能描述反悔的决策:
- 只剩 \(y\),意味着 \(2\) 个 \(x\) 没被兑换,即买了 \(2\) 个 \(x\)。
- 只剩 \(2x-y\),意味着买了 \(y\) 兑换 \(2\) 个 \(x\)(相当于免费买 \(y\) 兑换 \(2x-y\)),多出来的 \(2\) 个名额对应 \(y\) 和 \(2x-y\)。
- 都剩下,意味着买了 \(y\),用别的名额兑换 \(2\) 个 \(x\),名额数目依然对得上。
这一步非常抽象,对一对脑电波才行。
\(P\) 的操作:加入 \(y\) 和 \(2x-y\)。
-
\(r=1\)
只有保留兑换 \(y\) 的可能,这样肯定比兑换 \(x\) 或者买 \(y\) 更优。
\(P\) 的操作:加入 \(y\)。
-
注意:每个 \(x\) 操作完所有 \(y\) 才能执行加入操作,否则可能出现买 \(x\) 兑换 \(x\) 的情况。
至于为什么是小根堆,因为反悔之前兑换的更少的肯定更优。
时间复杂度 \(O(n\log n)\)。
数论专区
SP34112 UDIVSUM - The Sum of Unitary Divisors
\[\sigma^*(x)=\sum_{d|x}[\gcd(d,\frac{x}{d})=1]d \]求:
\[\sum_{i=1}^n \sigma^*(i) \]答案对 \(2^{64}\) 取模。
\(n\le 5\times 10^{13}\),数据组数 \(T\) 详见原题。
时限 \(10s\)。
定义
则有:
整除分块计算 \(f(x)\)。时间复杂度:
然后恭喜你被卡常了。
整除分块有一个 卡常trick。
观察里面那张图:
然后还是 TLE,经过实验发现被 Input 1:\(1\le n\),\(T\le 5\times10^4\) 卡掉了。
\(O(n\sqrt n)\) 打出 \(1\sim 5\times 10^4\) 的 \(f\) 即可。
交互
P7824 「RdOI R3」毒水
有 \(n\) 瓶水,其中有一瓶有毒。
有 \(k\) 只小白鼠,其中恰好有一只是变异鼠。
每只小白鼠能被用做一次实验,让它喝若干瓶水:
- 非变异鼠喝到毒水会死。
- 变异鼠喝不到毒水会死。
找到哪瓶是毒水。
注意:所有实验做完才会返回实验结果。
subtask 分值 \(n\) \(k\ge\) \(1\) \(1\) \(=1\) \(0\) \(2\) \(9\) \(\le 1000\) \(3000\) \(3\) \(20\) \(\le 1000\) \(30\) \(4\) \(30\) \(8\le n \le 16\) \(7\) \(5\) \(40\) \(\le 1000\) \(15\)
subtask 2 提示我们:\(3\) 只小白鼠能够确定一瓶水是否有毒。
trick:二进制分组。
假如无变异鼠,第 \(i\) 只小白鼠喝二进制第 \(i\) 位是 \(1\) 的编号的水,就能 \(\log n+1=10\) 次确定。
现在有变异鼠,可以继续使用 \(3\) 只确定一个二进制位,就是 subtask 3。
\(3\) 只一位太浪费了,还是使用 \(1\) 只一位,称这 \(\log n+1=10\) 只为 \(A\) 类鼠。
二进制分组套二进制分组,用 \(\log(|A|)+1=4\) 只去二进制分组喝 \(A\) 类鼠喝过的水,称为 \(B\) 类鼠。
但我们并不知道变异鼠 \(A\) 类、\(B\) 类还是剩下的 \(1\) 只。
用剩下的一只去喝 \(B\) 类鼠喝过的奇数次的水,称为 \(C\) 鼠。
-
\(B+C\) 有奇数只被毒死。
\(B+C\) 每瓶水都喝了偶数次,应毒死偶数只非变异鼠,这说明这之中有一只变异鼠。
\(A\) 类鼠都是非变异鼠,用它们判断。
-
后 \(5\) 只有偶数只被毒死。
同理,说明 \(A\) 类有变异鼠。
用 \(B\) 类鼠检验出它和它所管辖的 \(A\) 类鼠状态是否死了奇数只,以检测出变异鼠。
编号用 \(0\sim n-1\),能省下一点二进制分组。
杂项
CF1545D AquaMoon and Wrong Coordinate
有 \(n\) 个人站在一根数轴的正半轴的整点上,向数轴正方向做匀速直线运动。
给出他们 \(0\sim k-1\) 时刻的坐标集合(顺序有可能被打乱)。
这 \(nk\) 个数当中有一个数被修改成了任意值,求这个数位于哪个时刻的坐标集合和它原来的值。
保证第 \(0\) 时刻不会被修改。
\(5\le n\le 10^3\),\(7\le k\le 10^3\)。
以下认为时刻 1-index,即时刻是 \(1\sim k\)。
用 \(i\) 时刻坐标和减去 \(i-1\) 时刻坐标和能得到位移和,位移和不对的那个时刻就是被修改的时刻。
通过位移和能够知道修改后这个数变大了多少。
逆天观察:考虑一下平方和有什么规律。(天知道是谁想出来的)
记 \(f(t)=\sum\limits_{i=1}^n {x_{t,i}}^2\)。
-
\[f(t)-f(t-1)=\sum_{i=1}^n {x_{t,i}}^2-(x_{t,i}-v_i)^2=2\sum_{i=1}^n x_{t,i}\cdot v_i-\sum_{i=1}^n {v_i}^2 \]
-
\[f(t+1)-f(t)=\sum_{i=1}^n (x_{t,i}+v_i)^2-{x_{t,i}}^2=2\sum_{i=1}^n x_{t,i}\cdot v_i+\sum_{i=1}^n {v_i}^2 \]
进行一波减法:
这是一个定值。
然后拿出刚才找到的被修改的时刻 \(s\) 和修改的增加量 \(\Delta\)。
枚举 \(i\),\(x_{s,i}\gets x_{s,i}-\Delta\),检查 \(f(s-1)+f(s+1)-2f(s)\) 是否合法,就能找出修改的 \(x_{s,i}\)。
注意一些细节:
-
不存在 \(f(k+1)\),故 \(s=k\) 时检查 \(f(k)+f(k-2)-f(k-1)\) 是否合法。
-
选取连续三个正确的 \(f\) 以确定那个定值 \(2\sum\limits_{i=1}^n {v_i}^2\),\(s>3\) 选取 \(1,2,3\),否则选取 \(4,5,6\)。