2025年2月杂题集
2025年2月杂题集
目录
- P5904 [POI 2014] HOT-Hotels 加强版
- P10085 [GDKOI2024 提高组] 染色
- P1935 [国家集训队] 圈地计划
- [ARC107F] Sum of Abs
- P9878 [EC Final 2021] Check Pattern is Bad
- CF888G Xor-MST
- P4098 [HEOI2013] ALO
- P4585 [FJOI2015] 火星商店问题
- [AGC023F] 01 on Tree
- P9678 [ICPC 2022 Jinan R] Tree Distance
- P4757 [CERC2014] Parades
- P5065 [Ynoi2014] 不归之人与望眼欲穿的人们
- ARC192C - Range Sums 2
- P5279 [ZJOI2019] 麻将
- P4336 [SHOI2016] 黑暗前的幻想乡
- P3317 [SDOI2014] 重建
- P4455 [CQOI2018] 社交网络
- P6097 【模板】子集卷积
- AT_arc100_c [ARC100E] Or Plus Max
- AT_abc212_h [ABC212H] Nim Counting
- jzoj 7366 可怜的木偶 (dance)
- SP11470 TTM - To the moon
- AtCoder WTF2019 A
- AT_awtf2024_d Almost Bubble Sort
- P7771 【模板】欧拉路径
- P6628 [省选联考 2020 B 卷] 丁香之路
- AT_agc018_f [AGC018F] Two Trees
- ARC193B Broken Wheel
- P8528 [Ynoi2003] 铃原露露
- P9166 [省选联考 2023] 火车站
- P2391 白雪皑皑
Part 1
P5904 [POI 2014] HOT-Hotels 加强版
长链剖分与 DP 练习题。首先推出 \(O(n^2)\) 的 DP 式子,由于第二维都与深度有关,所以可以使用长链剖分优化成 \(O(n)\)。
对于长链上的儿子直接用指针继承状态,注意要精细实现才能不发生内存混乱。而对于非长链上的儿子则暴力合并。有点类似启发式合并的感觉。
有一些转移式子是枚举两个儿子,需要开一个临时数组记录前缀和优化复杂度。
P10085 [GDKOI2024 提高组] 染色
染一次是这样:
.o.
ooo
.o.
如果再把周围四个方向都染上就会变成:
..o..
.....
o.o.o
.....
..o..
我们把它称作 \(1\) 长十字架。
以此类推,每 \(5\) 个 \(2^x\) 长十字架可以组成一个 \(2^{x+1}\) 长十字架。
注意到如果我们放一个 \(2^{n-1}\) 长十字架,那么左右的就会重合,上下的就会重合,于是就实现了染一个点。
于是我们依次缩小十字架的长度,最后就能知道哪些点染的是 \(0\) 长十字架。复杂度 \(O(2^{2n}n)\)。
P1935 [国家集训队] 圈地计划
类比文理分科。变成最小割模型,割完后与 \(S\) 相连则选 \(A\),与 \(T\) 相连则选 \(B\)。
注意到这道题是相邻不同有额外贡献,而不是相同,那么我们可以先黑白染色,黑点是左 \(A\) 右 \(B\),白点是左 \(B\) 右 \(A\)。
此时额外贡献就很好表示了,还是从 \(S\) 向新增点连一条容量为 \(C\) 的边,从新增点向相邻两个点连容量为 \(inf\) 的边,另一边也同理。
最后答案就是总和减去最小割。
[ARC107F] Sum of Abs
还是最小割模型,不过建模需要仔细斟酌。
最理想情况下,答案应该是 \(-(\sum _{b_i<0} b_i)+\sum_{b_i\ge 0} b_i=\sum|b_i|\),而实际上很难达到理想状态,所以我们可以从理想状态调整建模:
- 删掉这个点,使答案减少 \(a_i+|b_i|\)。
- 一个 \(b_i<0\) 的点放入取正的连通块,使答案减少 \(-2b_i\)。
- 一个 \(b_i\ge 0\) 的点放入取负的连通块,使答案减少 \(2b_i\)。
所以把一个点拆成两个点 \(in_i,out_i\),左右连 \(S\) 和 \(T\),中间连上删点的贡献。
我们还有一个连通块内正负性相同的限制,所以如果 \((u,v)\),那么 \(out_u\to in_v,out_v\to in_u\)。
P9878 [EC Final 2021] Check Pattern is Bad
构造题。
Part 1 判断原图是否已经不合法。
Part 2 首先把能确定的都确定了,即把形如
WB
B?
这样的可以直接将问号换成 B。
我们正反扫两次就能把这些填完。过程中如果出现填两个颜色都不合法的格子也要输出 NO。
Part 3 每次对于剩下的问号,随机填,每填一个就从它开始搜索把能确定的都确定了。注意可能不合法需要回溯,因此要记录搜索时填了哪些位置。
Part 2
CF888G Xor-MST
看到异或首先想到 01-Trie。考虑 Kruscal 的过程,每次找到一条最小的边,看是否能连上。
发现在 01-Trie 上,两点的异或和主要与它们的 LCA 深度有关。
因此我们可以联想到,\(n\) 个点的 01-Trie 恰好有 \(n-1\) 处分叉,即拥有两个儿子的节点。而这些分叉就是答案 MST 的边的端点的 LCA。
现在我们只要对每一个分叉,求出在左右子树中各选一个点,求最小的异或和,可以枚举 Size 较小的一个子树,然后在另一个子树内查询。根据启发式合并的思想,这样枚举是均摊 \(\log\) 级别的,而查询也是 \(\log\) 级别的因此最终复杂度是 \(O(n\log n\log V)\)。
我们可以事先对 \(a\) 数组排序,然后在全局的 Trie 上做这些过程,这样就省去了启发式合并 Trie 的过程。
P4098 [HEOI2013] ALO
枚举一个 \(i\),考虑当 \(a_i\) 为次大值时,求异或的最大和。
可以用 ST 表加二分求出前驱最大值和后继最大值,然后我们需要区间查询异或和的最大值,用可持久化 Trie 即可。
时间复杂度 \(O(n(\log n+\log V))\)。
P4585 [FJOI2015] 火星商店问题
写起来感觉有一点 shi 的数据结构题。
首先有一个线段树套可持久化 Trie 的做法,对商店开线段树,对时间可持久化,这样时间和空间都是 \(O(n\log ^2n)\),很不优美。
有一个使用线段树分治可以做到时间 \(O(n\log ^2n)\),空间 \(O(n\log n)\) 的做法。
我们把询问的时间区间插到线段树上,于是一件物品就是单点插入,可以用 vector 按商店编号排序,自顶向下更新询问。
01-Trie 则显然就是按商店编号可持久化了。这样做每到一个节点会开一个 Trie,而用完就删掉了,因此空间可以少一个 $\log $。
[AGC023F] 01 on Tree
被称作 Exchange Argument(临项交换)技巧的贪心题。
这道题和之前 abc 的某道 G 题很像。
考虑自顶向下删点很难做,我们可以从独立的 \(n\) 个点开始,每次选择一个点,令它与其父亲连边,表示它的父亲删完以后,立刻删掉它所在的连通块。而每当与 \(0\) 号点(\(1\) 的父亲)连通时就代表执行删除连通块的操作。
而我们需要确定一个连边的顺序,这里用了 Exchange Argument 的 Trick。如果连通块 \(a,b\) 有 \(a_0,b_0\) 个 \(v=0\) 的点,\(a_1,b_1\) 个 \(v=1\) 的点,那么删 \(a\) 比删 \(b\) 更优当且仅当,\(a_1\times b_0<b_1\times a_0\),即
于是将 \(\frac {a_1} {a_0}\) 插入优先队列中即可。当 \(a_0=0\) 时根据题意看作无穷大。
每次取出优先队列的 top,然后将其合并到它父亲的连通块,使用并查集维护连通块。
由于还要计算答案,需要在每个连通块维护一个 \(s\) 表示删除连通块内的点的贡献,合并时直接将 \(s\) 相加并且计算额外贡献即可。
P9678 [ICPC 2022 Jinan R] Tree Distance
双倍经验:P9058 [Ynoi2004] rpmtdq。
以下 \((i,j)\) 代表 \(dist(i,j)\)。
我们把 \((i,j)\) 看做点对 \(i,j\) 的权值,那么原题转化为了二维数点问题,直接做是 \(O((q+n^2)\log n)\)。
瓶颈在于点对有 \(O(n^2)\) 个,注意到很多点对是没有用的,实际上有用的点对很少,我们可以找出支配点对。
如果点对 \(x,y\) 和 \(a,b\) 满足 \(a\le x\le y\le b\land(x,y)\le (a,b)\) 则 \(a,b\) 显然是没有用的,我们称 \(a,b\) 被 \(x,y\) 支配。
如果点对 \(x,y\) 不被任何另一对点支配,则 \(x,y\) 称作支配点对。
我们可以找到一种支配点对的必要条件。将所有可能为支配点对的点对加入集合 \(S\) 并且要使 \(S\) 的大小可接受。
由于是与点对有关的信息,考虑点分治,设当前分治重心为 \(rt\)。我们把当前连通块内任意两点 \(x,y\) 的距离看做 \((x,rt)+(y,rt)\) 即可,因为如果一个支配点对 LCA 不为 \(rt\),则可以在其他分治重心被统计到。
先给出结论:对于 \(x<y<z\) 且 \((x,rt),(y,rt)\le (z,rt)\),点对 \(x,z\) 一定不是支配点对。\(x>y>z\) 也如此。在这里 \(x,z\) 和 \(y,z\) 都在 \(rt\) 的不同子树内,否则可以在其他重心被统计到。证明如下:
\((x,y)\le(x,rt)+(y,rt)\le(x,rt)+(z,rt)=(x,z)\)
所以 \(x,z\) 被 \(x,y\) 支配。
因此只要对 \(x\),将满足 \(y<x\) 且 \((y,rt)\le(x,rt)\) 的最大 \(y\) 与满足 \(y>x\) 且 \((y,rt)\le (x,rt)\) 的最小 \(y\) 与 \(x\) 组成点对加入 \(S\) 即可。
可以将连通块内的点排序后使用单调栈维护。
这样连通块内的每个点都有 \(O(1)\) 个点对,而点分治均摊有 \(O(n\log n)\) 个点,就有 \(O(n\log n)\) 个点对。
剩下的就很简单了,离线后对 \(l\) 扫描线,用树状数组维护 \(r\) 的前缀最小值做二维数点。
时间复杂度 \(O(n\log ^2n+q\log n)\)。
Part 3
P4757 [CERC2014] Parades
模拟赛场切的一道题。
由于贪心不可做,所以考虑 DP,首先想设 \(f_x\) 表示子树 \(x\) 内最多选多少条路径。
那么考虑把路径挂在 LCA 上选,对于当前点 \(x\),对于一条路径 \((u,v)\) 满足 \(\text{LCA}(u,v)=x\),选这条路径的贡献就是 \(f_u+f_v+1\) 再加上这条路径上挂着的那些子树的贡献。
除此之外选路径 \((u,v)\) 还要满足 \(x\) 的在 \(u\) 和 \(v\) 方向上的边没有选,于是我们改进状态,加入状态压缩,设 \(f_{x,S}\) 表示选了儿子集合 \(S\) 的答案。
我们可以先用这些路径初始化 \(f\),然后再 \(O(3^{10})\) 枚举子集合并 \(f\)。
最后的问题如何算路径的贡献,有了状态压缩的 \(f\) 后就很容易做这个问题,对于 \(x,fa_x\),我们只要把 \(f_{fa_x,U-x}\) 加到 \(x\) 的子树中即可,其中 \(U\) 表示全集。
可以使用树状数组运用差分做子树加、单点查。
时间复杂度 \(O(3^{10}n+(m+n)\log n)\)。
P5065 [Ynoi2014] 不归之人与望眼欲穿的人们
模拟赛没做出来的一道分块好题。
首先有一个性质:固定区间左端点后,不同的右端点最多有 \(O(\log V)\) 个不同的或值,其中 \(V\) 是值域。
那么据此可以写出一个 \(O(qn\log V)\) 的做法并得到 70pts。
我们现在要处理出一个数组 \(b_i\) 表示区间长度小于等于 \(i\) 时按位或的最大值是多少。这样查询时即可直接二分。
由于只有 \(O(n\log V)\) 个不同的或值,所以考虑把区间的左端点往右移的过程中,维护出每个不同的或值及其位置。
可以用链表实现,当插入左端点 \(l\) 时,将所有位置或上 \(a_l\),将 \(l\) 作为链表的头,然后对于相同的或值只保留最前面的一个。
每次修改都这样暴力重构。
考虑改进这个做法,瓶颈在于修改时所有位置都扫了一遍太浪费了。考虑分块,设块长为 \(B\)。
那么合法的区间分为在一个块中和跨越多个块的情况。
在一个块中
修改时用上面的做法暴力重构一个块,查询时枚举每个块接着做二分,复杂度 \(O(q B\log V)\)。
跨越多个块
还是运用上面的性质,一个块内的前缀或后缀只有 \(O(\log V)\) 个不同的或值,那么总共有 \(O(\frac nB\log V)\) 个不同的或值。考虑这样做:
记 \(pos_i\) 为所在块的编号,\(l_x,r_x\) 为块的左右端点。我们把一个区间 \([L,R]\) 拆成 \([L,l_{pos_R}-1][l_{pos_R},R]\) 分别贡献。
那么我们可以枚举块 \(x\),用以 \(l_x-1\) 结尾的后缀与 \(l_x\) 开头的前缀合并在一起做贡献,由于前者与后者都有最多 \(O(\log V)\) 个或值且都单调,所以可以双指针做。这一部分是 \(O(q\frac n B\log V)\)。
后者可以每次暴力重构时处理出一个 \(pre_x\) 数组。
前者可以在从小往大枚举 \(x\) 的过程中,用类似上面的方法来做,每次加入一个块时,加入其中 \(O(\log V)\) 后缀,可以在重构时处理出一个 \(suf_x\) 数组,然后将之前所有值或上这个块的或和,然后将重复元素删去,一样用链表维护。这一部分是 \(O(q\frac n B \log V)\)。
而处理 \(pre_x,suf_x\) 都是 \(O(qB\log V)\)。
取 \(B=O(\sqrt n)\) 时可以做到 \(O(q\sqrt n\log V)\)。
ARC192C - Range Sums 2
简单交互题,由于 \(p_1<p_2\),我们问出 \(S=q(1,2)\) 后,对于 \(3\le i\le n\),分别问出 \(q(1,i),q(2,i)\)。
如果 \(\max(q(1,i),q(2,i))<S\) 则 \(p_1<p_i<p_2\),否则也可以知道在那一侧,然后根据大小关系就可以确定顺序。根据顺序就可以知道 \(a\) 数组。
当 \(p_1+1=p_2\) 时,可能需要多问一次 \(q(1,k)(p_k=p_1-1)\) 或 \(q(2,k)(p_k=p_2+1)\) 来确定 \(a_{p_1}\) 和 \(a_{p_2}\)。
P5279 [ZJOI2019] 麻将
DP 套 DP 练习题,用时 2h10min。
首先我们先建一个能不能判定当前局面是否能胡的自动机,然后在自动机上 DP 即可。在此之前可以先写出判定的 DP 式子然后建自动机。
首先我们只需考虑每种牌的个数。我们先来判定五个面子的情况,设 \(f_{i,j,k,0/1}\) 表示考虑完前 \(i\) 种牌后,当前预留了 \(j\) 对 \((i-1,i)\),\(k\) 个 \(i\),有没有预留对子,值为当前最大的面子数。
注意到到 \(j\ge 3\) 时,可以把三张 \(i-1\) 和三张 \(j-1\) 分别组成两个面子,\(k\ge 3\) 时可以直接组成面子,所以 \(j,k<3\)。
于是自动机上的每个点需要记录两个 \(3\times 3\) 的矩阵 \(f_0,f_1\)。另外我们不需要记录 \(i\),因为自动机上并不关心考虑到哪种牌。
另外对于七个对子的情况,只需再在自动机上的每个点记录当前遇到的对子数 \(k\)。
那么一个节点胡了当且仅当存在一个 \(f_1\) 中的值大于等于 \(4\),或者 \(k\) 大于等于 \(7\)。
实现可以使用 map 里面套两个 \(3\times 3\) 的 vector 和一个 int。注意初始化时只有 \(f_{0,0,0},k=0\),其他设为 \(-1\)。把所有胡的节点当做 \(0\) 号即可。
转移时枚举新增 \(x\) 张牌。有几种如下转移:
- \(f_0\) 新增 \(x\) 张牌,转移到 \(f_0'\)。
- \(f_1\) 新增 \(x\) 张牌,转移到 \(f_1'\)。
- \(f_0\) 新增 \((x-2)\) 张牌,转移到 \(f_1'\)。
- 当 \(x\ge 2\) 时 \(k\gets\ k+1\)。
转移时再枚举与 \((i-2,i-1)\) 配成面子的张数、与 \(i-1\) 配成 \((i-1,i)\) 的张数、留下 \(i\) 的张数,剩余的拼成尽可能多的面子。
注意 \(f_0\) 和 \(f_1\) 中的值要对 \(4\) 取 \(\min\) 才能保证点数,且 \(-1\) 不能参与转移。
发现算上初始点和胡节点只有 \(2092\) 个点。直接 DP 即可。
我们不要枚举排列,而枚举每种牌选的个数,通过阶乘算出排列的贡献,具体来说设 \(f_{i,j,k}\) 表示考虑完第 \(i\) 种牌后,选了 \(j\) 个额外的牌,走到了自动机的 \(k\) 号节点的方案数。
那么设初始有 \(a_i\) 张 \(i\) 号牌,则转移枚举 \(0\le p\le 4-a_i\),有
最后设 \(g_i\) 为额外选了 \(i\) 张牌后还没有胡的方案数。答案为
可以理解为前者是第一张牌,后者是选了 \(i\) 张牌后再选一张的概率。
时间复杂度为 \(2092\times 4\times n^2\)。
P4336 [SHOI2016] 黑暗前的幻想乡
容斥+矩阵树定理。
矩阵树定理(无向图)
矩阵树定理用于求一张图的生成树个数(生成树点数等于原图点数)。
定义邻接矩阵 \(A\),\(A_{i,j}\) 表示 \(i,j\) 之间的边数,度数矩阵 \(D\),\(D_{i,i}\) 表示 \(i\) 的度数,拉普拉斯矩阵 \(L=D-A\)。
则生成树个数为 \(det(L_{[n]\backslash \{k\},[n]\backslash \{k\}})\),即 \(L\) 除去任意一行与一列后的行列式。
行列式求值
定义如下,其中 \(p\) 为枚举全部排列,\(\sigma (p)\) 为 \(p\) 的逆序对数。
直接计算是 \(O(n!)\) 的,但我们可以消元计算做到 \(O(n^3)\)。
以下 \(a,b\) 表示两行。
- 交换 \(a,b\) 行列式取反。
- \(a\gets a\times k\),\(k\) 为常数,行列式不变。
- \(a\gets a+k\times b\),\(k\) 为常数,行列式不变。
那么我们可以将原矩阵高斯消元成仅 \(A_{i,i}\) 有值的矩阵,此时 \(det(A)=\prod A_{i,i}\)。
本题
由于同一种颜色的边不能重复选,所以可以考虑容斥,\(2^{n-1}\) 枚举每种颜色是否选,答案即为
时间复杂度 \(2^{n-1}(n-2)^3\),当 \(n=17\) 时约为 \(2\times 10^8\)。
Part 4
P3317 [SDOI2014] 重建
如果直接套上矩阵树定理,这里度数矩阵即所有边权的和,发现 WA 了,为什么呢?我们考虑一下答案的构成:
答案应该是枚举所有树 \(T\)。
而矩阵树定理根据定义应该是所有可能树的边权乘积的和,即
那么后面那一坨东西怎么办呢?发现后面可以化成这样:
代入得
由于我们用了除法,当 \(p_i=1\) 时就爆炸了,所以当 \(p_i=1\) 时,可以考虑 \(p_i\gets p_i-eps\)。
时间复杂度 \(O(n^3)\),比较的 \(eps\) 取了 \(10^{-18}\),\(p_i=1\) 的 \(eps\) 取了 \(10^{-15}\)。
P4455 [CQOI2018] 社交网络
矩阵树定理(有向图)。
邻接矩阵 \(A_{i,j}\) 表示从 \(i\) 连向 \(j\) 的边数,度数矩阵分为出度矩阵 \(D_{out}\) 和入度矩阵 \(D_{in}\) 两种。
对于外向树(边从根连向叶子方向的树),以 \(k\) 为根的生成树个数为
对于内向树(边从叶子连向根方向的树),以 \(k\) 为根的生成树个数为
本题
直接套用矩阵树定理即可,本题是以 \(1\) 为根的外向树。
P6097 【模板】子集卷积
题意是计算
前面 \(j\lor k=i\) 就是 FWT 的或卷积,\(j\land k=0\) 等价于 \(|j|+|k|=|i|\)。可以考虑对每一种 \(popcnt\) 都开一个数组。
即设 \(A_i\to a_{|i|,i},B_i\to b_{|i|,i}\)。
那么我们可以计算
其中 \(*\) 表示或卷积。
那么最后答案即 \(C_i=c_{|i|,i}\)。
在计算 \(c\) 时,我们计算一下复杂度,如果枚举 \(i,j\) 再 \(O(n2^n)\) 卷积则复杂度为 \(O(n^32^n)\) 无法通过。
但是 FWT 有一个性质,变换后的数组是可加的,即
所以计算出所有 \(a\) 卷 \(b\) 在没有经过逆变换时的和,然后再一起逆变换即可,复杂度为 \(O(n^22^n)\)。
AT_arc100_c [ARC100E] Or Plus Max
SOS DP。
题意:对于每一个 \(i\) 计算。
我们可以转而计算对于每个 \(i\) 子集的最大值和次大值,使用 SOS DP 记录最大值和次大值即可。
时间复杂度 \(O(n2^n)\)。
AT_abc212_h [ABC212H] Nim Counting
FWT 的可加性、等比数列求和。
算法 1 做 \(n\) 次 FWT 卷积,时间复杂度 \(O(nK\log K)\)。
算法 2 由于 FWT 的可加性,我们可以只做一次 FWT 卷积,我们现在就是要对每一项求 \(a+a^2+a^3+\cdots+a^n\)。
使用矩阵快速幂写出以下式子可以做到 \(O(K\log K+K\log n)\)。
事实上 \(a+a^2+a^3+\cdots+a^n\) 是等比数列,使用等比数列求和公式:
设 \(a\) 为首项,\(r\) 为公比,\(n\) 为项数,其中 \(A_1=a,\frac {A_{i+1}}{A_i}=r\)。
当 \(r\ne 1\) 时。
\[\sum _{i=1}^nA_i=a\times\dfrac {r^n-1}{r-1} \]当 \(r=1\) 时。
\[\sum_{i=1}^nA_i=a\times n \]
时间复杂度 \(O(K\log K+K\log n)\)。
Part 5
jzoj 7366 可怜的木偶 (dance)
由于 \(k!\) 是 \([1,k]\) 所有数的倍数,所以可以先走到 \(0\)。然后要走到最小正整数。
给出结论,如果确定了 \(a\) 数组,则最后最小走到 \(\gcd_{i=1}^na_i\)。证明:
- 证明答案不小于 \(\gcd\):因为所有数都是 \(\gcd\) 的倍数,且初始时 \(k!\) 也是 \(\gcd\) 的倍数,所以不能走到不为 \(\gcd\) 倍数的位置,则不能小于 \(\gcd\)。
- 证明可以达到 \(\gcd\):考虑前 \(i-1\) 个数的 \(\gcd\) 为 \(d\),现在加入第 \(i\) 个数 \(a_i\),根据裴蜀定理 \(dx+a_iy=\gcd(d,a_i)\) 一定有整数解,即可以通过走若干次 \(d\) 和 \(a_i\) 走到 \(\gcd(d,a_i)\),因此 \(n\) 个数可以走到 \(\gcd_{i=1}^na_i\)。
考虑设 \(f(x)\) 表示 \(\gcd\) 为 \(x\) 的方案数。设 \(g(x)\) 为所有 \(\gcd\) 为 \(x\) 的倍数的方案数的总和,则
根据莫比乌斯反演,
最后的答案即为
根据实现可以做到 \(O(Tk\log k)\) 或 \(O(k\log n+Tk)\),足以通过此题。
更优的复杂度
展开得
枚举 \(y=\frac d x\)。
枚举 \(xy\)。
根据狄利克雷卷积 \(\sum _{j|i}j\times \mu(\frac i j)=\varphi(i)\)。因此答案为
套上数论分块可以做到 \(O(k\log n+T\sqrt k)\)。
SP11470 TTM - To the moon
区间加的可持久化线段树。
调了很久终于发现了问题所在:由于用了标记永久化,标记不下传,因此不能从两个子树合并贡献,需要在修改下传时在每个节点加上贡献。
void update(int &x,int y,int l,int r,int L,int R,int z){
if(!x) x=++tot,ls[x]=ls[y],rs[x]=rs[y],tag[x]=tag[y],s[x]=s[y];
s[x]+=(ll)(min(R,r)-max(L,l)+1)*z;
if(L<=l&&r<=R) {
tag[x]+=z;
return;
}
if(L<=mid) {
if(ls[x]==ls[y]) ls[x]=0;
update(ls[x],ls[y],l,mid,L,R,z);
}
if(R>mid) {
if(rs[x]==rs[y]) rs[x]=0;
update(rs[x],rs[y],mid+1,r,L,R,z);
}
}
其中第三行 s[x]+=(ll)(min(R,r)-max(L,l)+1)*z; 很重要,并且最后不应该 s[x]=s[ls[x]]+s[rs[x]]。
AtCoder WTF2019 A
考虑到每次把 \(1\sim n\) 扫一次 \(K\) 就会减 \(1\),每次 \(K\) 减 \(1\) 我们称作一轮。
我们应该要做 \(K+1\) 轮就必定能找到宝藏。
考虑到 \(1\sim n\) 扫完后,最后一个开的盒子 \(x\) 一定没有宝藏,于是下一轮就可以不用扫 \(x\)。
也就是对于除了第一轮的每一轮,我们可以选择一个盒子不用开,因为这个盒子在上一轮的最后开过了。
那么令 \(b\) 为上一轮最后开的盒子,找到 \(nx\) 为满足与 \(b\) 不等且使 \(a_{nx}\) 最小的位置,那么扫一遍所有满足 \(i\ne nx\) 且 \(i\ne b\) 的盒子,最后再开 \(nx\),然后 \(b\gets nx\)。
每次开一个盒子时让 \(a_i\gets a_i-1\)。
当存在 \(a_i<0\) 时无解,否则一共开了 \(n(K+1)-K\) 次盒子。时间复杂度 \(O(nK)\)。
AT_awtf2024_d Almost Bubble Sort
相当于选一个下标集合 \(S\),使得 \(S\) 内元素 \(+n\),然后统计逆序对个数。
发现它又等于 \(inv(\{1\dots n\}\backslash S)+inv(S)+(\sum _{i\in S} n-i)-\binom {|S|}2\)。其中 \(inv(S)\) 表示子序列 \(S\) 的逆序对个数。即 \(S\) 的逆序对个数加上除去 \(S\) 的逆序对个数再加上对一个 \(01\) 排序的交换次数。
如果考虑把 \(n\) 个点表示成平面上的 \((i,p_i)\),那么发现最优选 \(S\) 的方案一定形如有一条从左下角开始的向右向上的折线,使得折线下方都 \(+n\)。
由于最后有一个关于 \(|S|\) 的贡献,所以如果直接 DP 并不好做。考虑设 \(c1_i\) 表示 \(i\) 左上方的点数,\(c2_i\) 表示 \(i\) 右下方的点数,那么选 \(S\) 就会贡献 \(c2_i+n-i\),并且最后的最后要加上 \(\binom {|S|}2\),而不选 \(S\) 就会贡献 \(c1_i\)。
我们可以初始让所有点都不选 \(S\),然后每次选一个点让它 \(+n\),那么选一个点的贡献为 \(f_i=c2_i-c1_i+n-i\)。那么对 \(f\) 排序后按顺序选即可,这种方式也能方便贡献 \(\binom {|S|} 2\),时间复杂度 \(O(n\log n)\)。
P7771 【模板】欧拉路径
欧拉路径:一个图中经过每条边恰好一次的路径,允许经过重复点。
欧拉回路:起点与终点相同的欧拉路径。
对于连通图,欧拉路径有如下判定:
- 对于无向图,恰好有两个点度数为奇数时,存在起点与终点不同的欧拉路径,且起点与终点就是这两个奇度数的点。
- 对于无向图,所有点度数均为偶数时,存在欧拉回路。
- 对于有向图,存在一个点 \(x\) 满足 \(deg_{out}+1=deg_{in}\),且存在一个点 \(y\) 满足 \(deg_{in}+1=deg_{out}\),且其他点入度等于出度,则存在起点与终点不同的欧拉路径,且 \(x\) 是起点,\(y\) 是终点。
- 对于有向图,所有点入度等于出度,存在欧拉回路。
寻找欧拉路径
基本思想:定义递归函数 \(dfs(x)\) 返回一个 \(x\) 的环,过程如下,先找到一个 \(x\) 的环 \(T\) 使得每条边都没有被走过,然后对于环上的每一个点 \(y\),将 \(dfs(y)\) 返回的环插入 \(T\) 中。
算法实际上不太一样,对于 \(dfs(x)\):
- 首先遍历每条未走过的出边(可能之前调用过 \(x\) 导致一些出边已走过)到 \(v\)。
- 调用 \(dfs(v)\)。
- 当所有出边都遍历完后,将 \(x\) 加入答案序列。
最后倒序输出答案序列即题目所需的答案。
本题还要求字典序最小,因此需要将出边排序,用 vector 记录出边。
实现上对于每一个点 \(x\) 记录 \(cur_x\) 表示当前弧,因为一个递归树中可能出现标号为 \(x\) 的点的子树内又出现了标号为 \(x\) 的点。
Part 6
P6628 [省选联考 2020 B 卷] 丁香之路
考虑题目要求的路径就是从 \(s\) 开始 \(i\) 结束的欧拉路径。我们先插入一条边 \((s,i)\) 把题目变成欧拉回路。
那么接下来就是要用最少的边权使原图变为合法的欧拉回路。
我们从 \(1\sim n\) 遍历 \(i\),如果 \(i\) 的度数为奇数,则连一条 \((i,i+1)\),发现这样连边是最优的,比把相邻度数为奇数的点相连更优,因为这样连边还可以使得图尽可能连通。
但图还有可能不连通,考虑把有边的点对用并查集缩点,相邻的点之间建边,然后跑最小生成树,发现欧拉回路的答案就会加上两倍最小生成树的权值。
时间复杂度 \(O(m+n^2+n\log n)\)。给定的 \(m\) 条边要预处理,因为如果每个 \(i\) 都枚举这 \(m\) 条边,复杂度将变成 \(O(nm+n\log n)\)。
AT_agc018_f [AGC018F] Two Trees
很厉害的结论题。
由于 \(-1\) 与 \(1\) 的奇偶性相同,如果一个编号在左树和右树中儿子个数的奇偶性不同就是无解。
否则我们可以这样构造:
先将两棵树的根连向一个超级根,那么现在所有节点都等价了。
对于度数为奇数的点,我们将它们在两棵树上的点连起来。
那么现在所有点都是偶数度数,我们跑出欧拉回路。
对于原本是偶数度数的点,\(X=0\)。
对于原本是奇数度数的点,如果额外的那一条边在回路上是从左走到右,那么 \(X=1\),否则 \(X=-1\)。
证明:对于树上一个点,欧拉路径进出其子树偶数次,其中一次经过其与其父亲的连边,剩余奇数次进出其子树内的额外边,且进入和出去的次数恰好相差 \(1\),那么因为进出的边权为 \(\pm 1\),所以这个点的子树和就是 \(\pm1\)。
ARC193B Broken Wheel
因为 \(\sum d\) 是一定的,所以确定了 \((d_0,d_1,\dots d_{n-1})\) 就能确定 \(d_n\),所以考虑计算它。
显然一种 \(d\) 序列可能对应多种连边方式。我们可以设 \(f_{i,0/1/2}\) 分别表示 \((i-1,i)\) 一定选连右、一定选连左、连左右均可的 \((d_{0},d_{1},\dots,d_{i-1})\) 方案数,这里的「连左右均可」计算的是满足连左或连右都可以被构造出的 \((d_0,d_1,\dots,d_{i-1})\)。
我们根据 \(s_{i-1}\) 的值讨论每一种可能的 \(d_{i-1}\) 即可转移。
那么我们指定 \((n-1,0)\) 连的是什么边,对应以下初始化与需要加上的值:
- \(f_{0,0}=1,f_{0,1}=f_{0,2}=0\),计算 \(f_{n,0}\)。
- \(f_{0,1}=1,f_{0,0}=f_{0,2}=0\),计算 \(f_{n,1}\)。
- \(f_{0,2}=1,f_{0,0}=f_{0,1}=0\),计算 \(f_{n,2}\)。
以上,发现会算重的情况当且仅当 \((d_0,d_1,\dots,d_{n-1})=(1,1,\dots,1)\),且 \((n-1,0)\) 连 \(0/1/2\) 都会被算一次。
所以最后将答案减 \(2\) 输出即可。
P8528 [Ynoi2003] 铃原露露
考虑对于 \((x,y,z=\text{lca}(x,y))\),如果 \(a_x<a_z< a_y\) 那么不会对不合法区间造成贡献,否则:
- 若 \(a_z<a_x<a_y\),则对于 \(l\in(a_z,a_x],r\ge a_y\) 的区间不合法。
- 若 \(a_x<a_y<a_z\),则对于 \(l\le a_x,r\in[a_y,a_z)\) 的区间不合法。
可以想到一个树上启发式合并的过程,维护每个点子树内 \(a\) 集合,当 \(x,y\) 在 \(z\) 处合并时,插入不合法的区间。
由于不合法的区间是包含 \(x,y\) 而不包含 \(z\),所以当 \(x\) 合并到 \(S\) 集合中时,只要查找 \(a_x\) 在 \(S\) 中的前驱与后继即可。
每次合并一个节点产生 \(O(1)\) 个支配对,所以支配点对的数量是 \(O(n\log n)\)。
接下来的问题形如给一个矩形加,然后查询矩形内为 \(0\) 的位置个数。
扫描线以后,就是区间加,差分后查询区间为 \(0\) 的个数的历史和。使用线段树维护。
具体来说,我们维护维护区间最小值、最小值的个数、\(0\) 的个数的历史和。
标记需要维护加标记、历史标记。历史标记表示区间内最小值会对历史和贡献多少次。
下传历史标记时需要知道哪些子区间是原先的最小值,因此上传时需要记录区间最值由哪些子区间转移而来。
每次如果全局的最小值为 \(0\),则对树根的历史标记加 \(1\)。
时间复杂度 \(O(n\log ^2n+q\log n)\)。
P9166 [省选联考 2023] 火车站
对于一条轨道 \(l,r\),我们 \(\forall i\in[l,r)\) 连无向边 \((i,i+1)\)。则只需统计与 \(x\) 连通的点即可。
由于轨道之间是没有区别的,所以可以差分实现区间加 \(1\),当 \(s_i>0\) 时表示 \((i,i+1)\) 之间有一条边。
查询则从 \(x\) 开始往左和往右走,往左只统计左端点,往右只统计右端点。时间复杂度 \(O(n)\)。
Part 7
P2391 白雪皑皑
由于后来的区间会覆盖之前的区间,我们考虑倒着做,那么每次就要覆盖区间内的 \(0\)。
考虑并查集维护一个点后继的 \(0\),时间复杂度 \(O(n \alpha(n))\)。

浙公网安备 33010602011771号