ZR2024 杂题选讲1 & 图论
其实就是 ZR 的十一集训。
2024
在我的深刻思考下,我决定先开 xtq 的杂题选讲,我能如期归来吗?
杂题选讲 by xtq
通信题因为我比较菜就没放上来了。
unknown
给定一棵带权树和一个 \(k\),选 \(k\) 个点标记,使得对于每个点(可以不是标记点)到最近的标记点的距离的最大值最小。\(n \leq 10^5\)。
先二分答案。考虑怎么 \(check\)。以下用“放”表示标记,\(mid\) 是二分的答案。
记 \(d_x\) 表示 \(x\) 的子树内至少放 \(d_x\) 个能使得我再在 \(x\) 处再放一个就能让子树全满足,\(f_x\) 表示再满足 \(d_x\) 最小的前提下 \(x\) 和它子树内最近的标记点的距离最小是多少(这里的地位是先让 \(d_x\) 最小然后再让 \(f_x\) 最小),\(g_x\) 表示如果 \(f_x\) 寄了(\(>mid\) 了)那么对于那些假如只标记那 \(d_x\) 点就无法满足的点中最深的那个点距离 \(x\) 是多少。
考虑如何合并子树。
首先对于 \(d_x\) 那些 \(d_{son}\) 是最基础的得加上,这个就是简单求和。
然后对于 \(f_{son}\) 考虑如果把这个儿子连向 \(x\) 的那个边加进来以后是否依旧 \(\leq mid\),如果是那么更新一下 \(f_x\)。否则的话如果当前的 \(f_{son}+w\) 全都 \(>mid\),那么就要更新变成 \(g_x=0\),\(f_x\) 照常更新取 \(min\)。
接下来是 \(g\),那么这个 \(g\) 就是我们要满足的,那么子树内显然是不行了,那么肯定是子树外的标记点覆盖它,考虑肯定是另一棵子树内的一个标记点转移过来,我们肯定希望这个标记点到 \(x\) 的距离最小(因为你考虑这个 \(son'\) 子树内的标记点到那个不满足的 \(g_son\) 的距离是\(f_{son'}+w_{son'}+g_{son}+w_{son}\)),那么你就看一下 \(f_{son'}+w_{son'}+g_{son}+w_{son}\) 这个东西是不是 \(\leq mid\) 的即可,这下就没有影响,反正都解决了。
然后如果不能覆盖到,也就是 \(f_{son'}+w_{son'}+g_{son}+w_{son}>mid\),此时如果 \(g_{son}+w_{son} \leq mid\),那么显然在 \(x\) 再放一个就完了,那么你更新一下 \(g_x\) 的值就行(用 \(g_{son}+w_{son}\))。如果 \(g_{son}+w_{son}>mid\),那么在 \(u\) 放也会寄。我们设 \(g_{son}\) 的那个最深的点是 \(p\),此时就得在 \(p-x\) 的路径上选一个点使得能覆盖到 \(p\) 并且距离 \(x\) 最近的点,这个也是好维护的,维护以后更新一下新的 \(f_x\) 并且把 \(d_x\) 加一即可。
qoj8544
有一个正 \(n\) 边形,连了一些不相交的对角线,给每个点黑白染色,使得没有一个环满足所有点同色。构造方案。
这个图可以三角剖分,那么考虑必然存在一个点 \(u\) 不在对角线上,考虑直接把这个点 \(u\) 删掉,处理剩下的子问题。假设现在已经处理好了删掉 \(u\) 的剩下点的方案,那么考虑 \(u\) 在正 \(n\) 边形上相邻的两个点 \(x,y\),对于经过 \(u\) 的环也必然经过 \(x,y\),因此直接看 \(x,y\) 如果一黑一白 \(u\) 就随便染,否则 \(u\) 就染相反的颜色即可。不停删掉这样的 \(u\) 递归子问题即可。
似乎有更好的实现方法。
qoj10404
给定 \(n\) 个区间 \({l_i,r_i}\) 和常数 \(c\)。两个区间可以匹配当且仅当存在 \(l_i \leq x_i \leq r_i,l_j \leq x_j \leq r_j\) 使得 \(x_i+x_j=c\)。求最大匹配。\(n \leq 2 \times 10^5,c \leq 10^9\)。
似乎是一种新式的扫描线,强大!
考虑对 \(x_i,x_j\) 做扫描线,钦定 \(x_i<x_j\),那么就是有两个指针分别从 \(0\) 和 \(c\) 往中间扫。考虑维护两边的包含对应 \(x_i,x_j\) 的区间。首先是左边 \(x_i\),要新加入左端点是 \(x_i\) 的,然后在 \(x_i\) 向右移动的时候把右端点是 \(x_i\) 的区间去掉,这些右端点是 \(x_i\) 我们考虑在这个时候给它和右边的一个区间匹配了,对于右边的所有满足条件的里面选一个左端点最靠右的,因为这些区间是最近的要消失的。如果没有可以匹配的区间了,就不管它了,等死吧。右端点同理,用相同的方式维护。
特殊的,对于包含 \(\frac{c}{2}\) 的这些区间,是可以直接两两匹配的,直接把答案里面加上即可。
unknown
有 \(n\) 个人,每个人有一个区间 \([l_i,r_i]\)。维护当前的集合 \(S\),初始为空。执行多轮:选择一些不在 \(S\) 的人满足 \(l_i \leq |S| \leq r_i\) 并加入到 \(S\) 里面。问是否能让 \(S\) 变成全集。\(n \leq 2 \times 10^5\)。
贪心,肯定是按 \(r\) 从小到大放,我们考虑维护 每次加进去一些人 这个事情怎么是最优秀的。我们对于当前的能放的所有人,首先是要优先放 \(r\) 小的,考虑先把能放的里面 \(r\) 最小的放上,然后考虑如果此时就不放了进入下一轮,那么有没有问题,如果有问题那么显然是由一些区间虽然在这一轮能放,但是当 \(|S|\) 加一以后就不能放了,我们再把这些区间加进来。以此类推,如果在当前的 \(|S|\) 变动之后还是有不合法的就继续放进来。这样一直做下去如果没发全都加进来就寄了。
这个贪心显然是对的,它的贪心思路是每次把必要的加上就行了,每次尽量加少一点。这个逻辑是正确的,证明你考虑如果一种方案它过程中加的一些人可以分两次加,显然是不劣的。
kilonova209
给定一个长度为 \(n\) 的数组 \(a\),你可以执行 \(k\) 次的给某个数加一,问最后数组的相邻数差之和的最小值。\(n \leq 10^6,a_i \leq 10^9\)。
你考虑怎么操作是有用的,只可能是下面的情况:

第一种操作可以使用 \(r-l+1\) 的代价让答案减 \(2\),第二种操作可以用 \(r\) 的代价让答案减 \(1\),第三种可以用 \(n-l+1\) 的代价让答案减 \(1\)。考虑建立笛卡尔树,此时一个点的子树就对应的是一个可以操作的区间(你得看一下是上面操作的哪一种)(为什么一定是一个可以操作的区间呢,因为此时对于一个最开始讲的可以操作的那种形式的区间,它对应在这里面肯定是一棵子树,然而对于任意一棵子树,它的子树内的小的子树肯定是能操作就操作完了的,所以它们肯定都是变成了子树根的值的,所以如果你要操作这一棵子树,那么它一定是满足那个形式的)。我们令一个可以操作的子树的根的值为 \(z\),它对应的左右两边的第一个比它大的是 \(x,y\),那么对于这个子树就最多可以操作 \(x-z\) 次。
所以你把所有可以操作的东西分成两部分:
- 消耗 \(w\) 的代价让答案 \(-1\)。
- 消耗 \(w\) 的代价让答案 \(-2\)。
这个有两种做法:
-
按照性价比贪心。但要考虑一种特殊情况:把一个 \(-1\) 去掉换成 \(-2\)。
-
两种物品都按 \(w\) 排序,那么显然是会分别取一个前缀,那么枚举其中一个前缀,另一个前缀最长取到哪里二分一下即可。
我还是有些一知半解,等后面再仔细思索一下吧。
kilonova1830
给定一个长度为 \(n\) 的排列,你可以执行两种操作之一:
- 交换相邻两个位置
- random shuffle 整个序列
给定 \(k\) 问至多操作 \(k\) 次后在最优的操作下,最后排列逆序对个数的期望。
首先 \(k\) 肯定是得 \(O(n^2)\) 的,否则直接暴力排序即可。
注意到一些事情:
- 答案仅和 \(k\) 与排列的逆序对个数有关。
- 如果开始用 1 操作了那么一定是会用到底的。
那么令 \(f_{i,j}\) 表示还剩 \(i\) 次操作,当前逆序对个数为 \(j\) 的情况下,最后的答案。初始状态是 \(f_{0,j}=j\)。转移是 \(f_{i,j}=\mathrm{min}(\mathrm{max}(j-i,0),E_{k}(f_{i-1,k}))\)。其中 \(E_k(f_{i-1,k})\) 表示对于每个 \(f_{i-1,k}\) 乘上下一步逆序对个数为 \(k\) 的概率再求和的值。前面和后面分别表示第一步选择操作 1 和操作 2 的情况。
只要我们预处理 random shuffle 一次以后逆序对数 \(=k\) 的概率就是 \(O(n^4)\) 的,因为状态数是这个量级。
考虑把转移的 \(\mathrm{min}\) 里面的前后两个东西看成两个函数:

如图,我们用两个紫色点就能描述 \(f_{i,j}\) 当 \(i\) 固定时的所有值。转移也是十分的简单,把此时的描述方法映射到原来的转移里面即可。
一个小的点,如何预处理一次 random shuffle 以后逆序对数 \(=k\) 的概率?考虑令 \(f_{i,j}\) 表示长度为 \(i\) 的排列逆序对数为 \(j\) 的概率,那么转移就考虑第 \(i\) 个数是几即可。
unknown
给定一张无向无权图,起点为 \(S\),终点为 \(T\),
Alice想从 \(S\) 走到 \(T\),在走到的过程中(任意时刻),Bob有一次机会可以删掉一条边,问Alice最快多久能走到 \(T\)。\(n \leq 10^5,m \leq 10^6\)。
首先有一些基本的观察:肯定是在某一时刻删除和 Alice 当前所在地相连的边。证明是容易的。然后 Bob 肯定是要阻拦 Alice 行进的,那么她删的肯定是当前 Alice 所在点到 T 的最短路径上的一条边,因此假设我们建立以 \(T\) 为根的最段路树(\(bfs\) 树),那么 Bob 删掉的点一定是树上的一条边。我们设 \(d_x\) 表示在删掉了 \(x\) 和 \(fa_x\) 的边以后 \(x\) 和 \(T\) 的最短路径的长度。怎么求呢?此时在最短路径上是不能直接走通了,那肯定得通过一条横叉边到其它子树内然后再走到根(返祖边是不太可能的,不然直接可以替换掉最段路树上的一条路径了),设两个端点是 \(p,q\)(\(p\) 是子树内的,\(q\) 是另一棵子树的),那么此时走到 \(T\) 的这条路径长度就是 \(dep_p-dep_x+dep_q+1\),对所有的 \((p,q)\) 的这个取 \(\mathrm{min}\) 就是 \(d_x\)。这里把 \(1-dep_x\) 拿出来,剩下的就是和 \((p,q)\) 相关的了。考虑枚举每一条横叉边 \((p,q)\) 反过来更新 \(d_x\),那么就是对于 \(lca(p,q)\) 到 \(p\) 上不包含 \(lca(p,q)\) 的路径的点的 \(d\) 取 \(\mathrm{min}\)。这个数据结构维护一下即可。
但现在还有一个问题,就是难道答案真就只用枚举删的那个边(假设是 \(x\) 和 \(fa_x\) 的边),就是 \(\mathrm{min}(d_x+\mathrm{dis}(S,x))\) 吗?并不这样,因为我们并不能决定 Bob 是不是一定在这个时候删,Bob 是大坏蛋!她很可能在之前就把我们超掉了呜呜。所以此时 xtq 巨佬给出了一个超级厉害的方法:我们假定一个 Alice 的计划走的路径 \(v_1,v_2,...,v_m\),那么此时的答案就是 \(\mathrm{max}(d_{v_1},d_{v_2}+1,...,d_{v_m}+m-1)\),那么就是对每一条路径的这个东西取 \(min\) 就是答案了。
于是我们可以二分答案,那么就是要 \(check\) 是否有一个路径满足 \(d_{v_1} \leq mid,d_{v_2}+1 \leq mid...\)。那么其实就是对于每个点有一个限制 \(p_x=mid-d_x\) 要求在走到 \(x\) 的时候最多走了 \(p_x\) 步。于是我们记录 \(f_x\) 表示到 \(x\) 的最小步数,那么就直接类似于 \(bfs\) 扩展即可:
u
rep v: u
if d[v]+f[u]+1<=mid
f[v]=f[u]+1
注:在 \(d_x\) 的求解部分我写的和 xtq 巨佬写的有些出入,但是我在深刻思考后觉得我写的是对的,xtq 写的可能是错了扒。
另外说一句,为什么都是这种神仙题啊呜呜。
unknown
给定长度为 \(n\) 的排列和常数 \(k\),问至少交换多少次能把 \(1,2,3,...,k\) 排好序。
\(k \leq n \leq 2 \times 10^5\)。
我们不知道最后 \(1\) 到 \(k\) 的位置在哪里,不好直接做。考虑做一步转化,把 \(k+1\) 到 \(n\) 的值赋值为 \(0.5,1.5,...,k+0.5\),其中 \(0.5\) 的插在 \(1\) 前,\(1.5\) 插在 \(1,2\) 之间,以此类推,也就是要给整个序列排序,此时的操作次数就是逆序对个数。
现在问题变成要给 \(k+1\) 到 \(n\) 的位置赋值 \(0.5,1.5,...,k+0.5\),要让序列逆序对个数最小,这个就是 AHOI 的那个逆序对那个题。
unknown
给定一个简单无向图中所有点的度数,输出有多少边一定在图中。
\(n \leq 5 \times 10^5\)
对于一个边 \((x,y)\) 它必然存在等价于在原图中加入这个边以后图不可能是简单无向图。这个证明有点厉害。如果加完以后不合法,那么显然必然有 \((x,y)\) 这个边。考虑如果加完以后合法,那么如果有一种方案包含 \((x,y)\) 那么去掉以后就是一种对于原度数序列的可行的但是不包含 \((x,y)\) 的方案,所以 \((x,y)\) 不必要。可能会没有这样的方案吗?不可能,考虑对于一种没有 \((x,y)\) 的方案 \(G\),我们把它与原度数序列的一个图 \(G'\)的方案做差(对于一条边,如果 \(G,G'\) 是否拥有的情况相同,那么就不管它,否则如果 \(G\) 存在就在差图中连一条实边,否则连一条虚边),那么对于 \((x,y)\) 来说,必然有一个环,然后环上的其它边虚实交替,然后 \((x,y)\) 是实边,那么我们直接对所有边取反以后就是一个包含 \((x,y)\) 的方案了。
判定一个图是不是合法的用 1 即可。然后算答案就是好算的了,直接枚举 \(x\) 然后看 \(y\) 有多少个就行了。
AGC068C
有 \(n\) 个球和 \(n\) 个箱子,初始所有球分布在这些箱子里面。
从 \(i=1,2,...,n\) 执行:考虑第 \(i\) 个箱子里面所有球 \(a_1,...,a_k\) 将 \(a_i\) 球放到第 \(a_{i+1}\) 哥箱子里。
给定最终状态,问是否可能达到。
\(n \leq 2 \times 10^5\)。
咕咕咕。
loj3594
给定一张无向图,
Alice和Bob在上面玩捉迷藏。
Bob每次给出一个点,问Alice在不在里面。
问完后Alice移动到相邻的点,求出Bob是否能在某一个时刻确定Alice在哪里。
\(n \leq 10^6\)。
可以认为是初始有一个点集 \(S\) 为全集,每次删除一个点,然后再把 \(S\) 变成邻域 \(\{v|u \in S,(u,v) \in E\}\)。问是否能删成空集。这里直接给出结论:当且仅当存在一条链使得所有点距离这条链不超过 \(2\) 的时候图合法。
如何证明呢?其实就是要证明它的充分性和必要性。必要性的话考虑给图黑白染色,每次就是翻转一些颜色,归纳证明即可。充分性其实只用考虑下面这个东西,因为这个是满足性质的情况下最简单的图:
|
|
|
|
/ \
/ \
/ \
/ \
装压一下可以证明不可以。
qoj9420
给定一张无向图,
Alice和Bob在上面玩捉迷藏。
Bob每次给出一个点集,问Alice在不在里面。
问完后Alice移动到相邻的点,求出Bob是否能在某一个时刻确定Alice在哪里。
\(n \leq 10^6\)。
unknown
给定序列 \(a_1,...,a_n\)。
对序列 \(b_1,...,b_m\),Alice和Bob玩一个游戏,Alice先手:
Alice操作时删除一个前缀并获得其中min的分数。Bob操作时删除一个前缀并获得其中max的分数。
有 \(q\) 个询问,问如果把 \(a_l,a_{l+1},...,a_r\) 拿来玩游戏,最优情况下score_{Alice}-score_{Bob}。
\(n,q \leq 2 \times 10^5\)。
观察到一件事情,在 Alice 拿完后 Bob 一定是直接拿完的,证明如下:

显然不一次取完是不优的。所以其实就是 Alice 取一次然后 Bob 取剩下的。因为 Alice 是先手,所以它肯定是取最优秀的,也就是说对于询问 \((l,r)\),答案就是 \(\mathrm{min}_{x=l}^r (\mathrm{max}_{i=l}^x a_i-\mathrm{max}_{i=x+1}^r a_i)\)。
直接做是不好做的,考虑分治,对于 \((l,r)\) 在分开的区间的其中一边的这种询问直接递归下去,现在要处理 \((l,r)\) 跨越 \(mid\) 的询问。
考虑对 \(r\) 做扫描线,我们 不妨设 \(x\) 在 \(mid\) 右边,对于使得后缀的那个 \(\mathrm{max}\) 相同的 \(x\) 一定取更小的 \(x\)(这样前缀 \(\mathrm{min}\) 最大),所以如果维护一个单调栈,那么答案的 \(x\) 一定在栈里面的位置取。
此时我们考虑对于 \(r\) 所要求的一个左端点 \(l\) 来说怎么用那些单调栈的 \(x\) 求答案,对于一个 \(x\) 来说它的区间内前缀 \(min\) 就是把 \(\mathrm{min}(a_l,...,a_mid)\) 拼上 \(\mathrm{min}(a_{mid+1},...,a_x)\),那么对于一个前缀的 \(x\) 这个东西是取前者,后缀是取后者。对于前缀的 \(x\) 来说此时的答案就是固定的 \(\mathrm{min}(a_l,...,a_mid)\) 减掉 \(\mathrm{max}_{i=x+1}^r a_i\),为了让这个东西最大,后者要最小,这个是好做的,我们在维护单调栈的同时维护一个前缀 \(min\) 即可。对于后缀的 \(x\) 来说此时答案就是关于 \(x\) 的一个柿子:\(\mathrm{min}(a_{mid+1},...,a_x)-\mathrm{max}_{i=x+1}^r a_i\) 注意到后面的东西如果变化了,那么说明它一定不在单调栈里了(如果变化说明新加进来一个 \(a_r\) 并且 \(a_r\) 大于它原本后面的柿子使得它后面的东西变成了 \(a_r\),这个时候显然它就会被 pop,或者说它没有利用价值了),所以一旦一个元素在单调栈里,我们就只用记一下它刚加进单调栈里的时候它所对应的柿子的值即可。这个要维护一个后缀 \(max\)(要对后缀的所有 \(x\) 所算出来的一坨柿子取 \(max\)),拿个什么数据结构硬维护就行了。
qoj149
给定序列 \(a_1,...,a_n\) 和常数 \(k\),每次可以选一个长度不超过 \(k\) 的区间并规定 \(x\),支付 \(x\) 的代价让区间内 \(\leq x\) 的数变成 \(0\),对于所有 \(i\) 求让前 \(i\) 个数变成 \(0\) 的最小代价。
\(n \leq 2.5 \times 10^6\)。0.5s
令 \(f_{i}\) 表示 \(i\) 的答案。注意到每次操作一定是删掉区间内所有数,也就是让 \(x\) 为区间 \(\mathrm{max}\)。那么就有转移 \(f_i=\mathrm{min}(f_{j}+\mathrm{min}(a_{j+1},a_{j+2},...,a_i))\) 其中 \(i-m \leq j \leq i-1\)。在转移里面的那个 \(\mathrm{max}\) 一样的情况下肯定是选靠前的,所以这里就是要维护所有后缀 \(max\) 变化的位置 \(x\) 作 \(j=x\) 转移过来的。这个东西其实就是单调栈。然而还要满足 \(j \geq i-m\),也就是说我们对于普通的单调栈还要维护一个删除栈底的操作。
梳理一下,需要维护的有:
- 基本的栈要做的在栈顶 pop / push。
- 在栈底 pop。
- 查全局 \(\mathrm{min}\)。
考虑维护一个类似双端栈的结构:

维护两个栈的 \(suf,pre\),查询就两边拼起来。
如果一边的栈删完了,就取另一个栈的中点再划分两个栈然后暴力重构。
qoj8542
给定初始序列 \(y_1,...,y_n\),还有一个初始全 \(0\) 的序列 \(x_1,...,x_n\),每次可以给它的前缀或后缀加 \(1\),代价是这个前后缀的长度,问最少花费多少代价让所有 \(x_i \leq y_i\)。
\(n \leq 10^6\)。
咕咕咕(xtq 没讲或者是我没看到)。
qoj8553
有一个隐藏的序列 \(a_1,...,a_n\),每次可以询问一个区间的最大子段和,要求返回一个序列 \(b\) 使得对于任意区间 \(a\) 和它的最大字段和一样,需要询问次数 \(\leq 2n\)。
\(n \leq 2000\)。
首先对于每个位置问一遍,可以的得到所有正数的值和位置(以及负数的位置,其它的就是)。根据最大子段和的性质,对于一段正的值和一段负的值一定是一起取的,我们考虑把每一段正负相同的数合并起来。下面记 - 表示一段负,+ 表示一段正。此时用了 \(n\) 次。
考虑从最小的正段开始问,询问它两边的两个 +-+,对于一个 +-+ 如果问出来和两个正段都不一样,那么就可以得到负段的值。如果全都不能确定,那么就可以把中间的 -+- 合并。
这样每询问两次,长度至少减 \(2\),最多用 \(n\) 次。于是就解决了。
图论 by xzy
P3916
给定一个有向图,对于每个点求出能到达的点中 id 最大的点的 id。
\(n \leq 10^5\)。
建反图,然后对于每个点求能被哪些点到达然后更新。考虑从 id 大的点更新,如果更新到了之前已经更新过的就直接 return 就行了。
CF547D
在一个平凡的坐标系里有 \(n\) 个点,给所点黑白染色使得每一条直线和竖线上黑点白点数量之差不超过 1 个。
给同一行的点两两配对连边,同一行也是,然后对这个二分图做黑白染色。可以发现这样就是对的。
CF1889D
初始有两个函数
init(i),get(i)和 \(n\) 个栈,每个栈里面有一些 \(1\) 到 \(n\) 的数。
get(i):取出第 \(i\) 个栈的栈顶,并将这个栈顶的数字作为新的 \(i\) 执行 \(get\),并返回这个 \(get\) 得到的值。如果 \(i\) 在之前已经空了,就返回 \(i\)。
init(i):复原所有栈,并返回get(i)的值。
对于 \(i=1,2,3,...,n\) 求出 \(get(i)\)。\(n \leq 10^5\)。
先思考栈大小为 \(1\) 的情况,此时连边 \(i \to c_{i,1}\),此时是一个内向基环树,此时每个点的答案就是不停的跳出边然后求第一个重复访问的点,其实就是走到的环上的第一个点。
考虑拓展,注意到环是可以忽略的,因为只要走到其中一个点就相当于是把整个环 pop 一遍然后回到原点,所以我们可以不断的删除环直到最后没有环。对于没有环的图显然一次 dfs 就能做完了。
具体的实现方面可以直接在原图上 dfs,如果访问到重复的点此时就有一个环,在 dfs 的过程中记一个栈,访问重复的时候栈里面的数就是环上的点。
CSP32 T5
给定一张有向图,每个点有颜色 \(c_i\),每条边有一个正边权,对于所有从 \(1\) 走到 \(n\) 的路径,点数不超过 \(L\),颜色互不相同的路径的边权和的最大值。
\(n \leq 100,m \leq 5000,k \leq 30,L \leq 9\)。其中 \(k\) 是颜色种类数。
solution1
枚举路径上第 \(3,5,7\) 个点,然后要填入 \(2,4,6,8\) 的点,根据 CSP-S2022 T1 的结论得到它们得是所有满足要求(和两边的奇数点有边)的点中和前后点边权和前 \(t\) 大的(\(t\) 取 \(6\)?),暴力维护即可。复杂度是 \(O(n^3)\) 但是有 \(t^4\) 的常数。
solution2
折半,枚举断点,前后的路径可以直接搜(这部分是 \(O(n^4)\)),现在思考怎么拼起来。拼起来的话对于前后的路径记一下路径上用的颜色集合 \(S,T\)。如果 \(S,T\) 没有交就可以尝试拼起来然后更新答案。然而是不能直接拼的,直接拼是 \(O(n^4+nk^6)\)。咋办呢,不直接枚举 \(S,T\) 的话,可以枚举 \(S,T\) 的前两种颜色,然后第三种颜色只用取前 \(4\) 大的。这样就做到了 \(O(nk^5+n^4)\)。
solution3
根据 P7450 的套路,考虑把所有颜色映射到 \(1\) 到 \(9\) 的新颜色,然后跑状压。怎么跑装压就略过了,很简单怎么写都行。正确率是多少呢?显然有下届 \(\frac{t!}{t^t}\),其中 \(t\) 是路径上的颜色数,因为 \(1,n\) 是固定的头和尾,所以这里 \(t\) 取 \(7\) 得到正确率是 \(\frac{1}{200}\) 左右,跑 \(1000\) 轮左右肯定就能过了。
CSP-S2022 T1 上大分!
传递闭包
给定有向图求所有点对之间的可达性。
注:这里假定图是稠密图即 \(m\) 和 \(n^2\) 同阶。
首先这个问题的时间复杂度有一个下界 \(O(\frac{n^3}{poly(n)})\),这个分子上的指数改进很难,真的是十分 Hard 的一个问题。下面的 \(w=64\) 即 bitset 所优化的二进制位数。
-
\(O(n^3)\)
直接
floyd。
-
\(O(\frac{n^3}{w})\)
直接用
bitset做floyd(对每个点记 \(bst_i\) 对应原来的 \(f_{i,j}\) 数组,转移就拿 \(f_i\) 去 \(\mathrm{or}\) 上 \(f_k\))。
-
\(O(\frac{n^3}{w\ \mathrm{log}\ n})\)
考虑对每个点 \(i\) 求它可以到达哪些点。
首先缩点,一个 scc 内的点可达情况是一样的。现在就是要 做DAG可达性。我们首先考虑一个朴素的做法,就是对于每个点维护它可以的到达的点的bitset。那么这个时候就可以直接按拓扑序从后往前做,每次就枚举当前点的出边然后 \(or\) 上出点的biset。这个做法是 \(O(\frac{nm}{w})\) 的,考虑优化它。先把拓扑序弄出来,然后对拓扑序分块。此时对于一个点 \(i\),我们可以先把它块内的出点的bitset的or暴力做出来(使用前面的朴素做法)。对于块外的,考虑对于所有的块预处理出的它的每个子集点的bitset的or。那么对于点 \(i\) 来计算块外的时候,就是对于它所在块的后面的块来说,提前计算出来 \(i\) 的出点在这个块内对应的是哪个子集,然后直接用这个块内所提前计算的bitset的or来计算即可。我们分析一波复杂度,设取块长 \(B\),那么复杂度就是可以分成三部分:
- 块内计算:\(n \times B \times \frac{n}{w}\)。
- 块内预处理:\(\frac{n}{B} \times 2^B \times \frac{n}{w}\)。
- 块外计算:\(n \times \frac{n}{B} \times \frac{n}{w}\)。
取 \(B=\mathrm{log}\ n\) 即可得到 \(O(\frac{n^3}{w\ \mathrm{log}\ n})\) 的复杂度。
qoj7519
给定一张 \(n\) 个点 \(\frac{n(n-1)}{2}-m\) 条边的无向图的补图,求原图内对于 \(k=1,2,3,...n-1\) 来说两点间最短路径长度为 \(k\) 的点对数量。
\(n \leq 10^5\)。
其实可以发现绝大多数点对的距离都很小,甚至很大一部分是 \(1\),然而怎么刻画这件事情成了一个问题,虽然你发现距离不为 \(1\) 的点对只有 \(O(n)\) 级别但是显然不能直接求。
有一个关键性质来破局:
对于两个点 \(x,y\),在补图中若 \(deg_x+deg_y<n\),那么显然存在一个点 \(z\) 使得 \(z\) 和 \(x,y\) 在补图中均没有边,也就是在原图中均有边,那么就有 \(dis(x,y) \leq 2\)。
对于这样的 \((x,y)\),显然如果 \((x,y)\) 在补图中有边,那么 \(dis(x,y)=2\) 否则 \(dis(x,y)=1\)。
对于不满足 \(deg_x+deg_y<n\) 即 \(deg_x+deg_y \geq n\) 的 \((x,y)\) 如何处理呢,此时必然有 \(deg_x,deg_y\) 其中一个不小于 \(\frac{n}{2}\),那么我们对于补图中 \(deg \geq \frac{n}{2}\) 的所有点跑一遍最段路即可。
P5304
给定一张 \(n\) 个点 \(m\) 条边的有向图,有 \(k\) 个关键点,求它们两两之间距离的最小值。
\(k \leq n \leq 10^5,m \leq 5 \times 10^5\)。
显然不能直接做多源最段路,因为这样显然跑出来每个点答案都是 \(0\)。所以现在问题的关键就是不能把“自己到自己”这种情况算上。下面是两种课件里面给出的解决方案:
solution1
为了避免自己到自己的情况,那么其实就是只想算 \(u,v\) 的最段路满足 \(u \ne v\),那么 \(u,v\) 的二进制中必然有一位不同。于是我们枚举所有二进制位,然后把二进制一位是 \(0,1\) 的关键点拿出来,然后分别以 \(0,1\) 和 \(1,0\) 做为起点和终点跑 dijk 即可。复杂度两只 \(log\)。
solution2
同样的套路,考虑分治。每次把 \([l,mid]\) 的点贡献向 \([mid+1,r]\)(以 \([l,mid]\) 的点为多源最段路起点,然后对 \([mid+1,r]\) 更新),以及 \([mid+1,r]\) 的点贡献向 \([l,mid]\),这样也可以有效避免自己贡献自己的情况。复杂度两只 \(log\)。
solution3
为了避免自己到自己,我们强制选一条边 \((u,v)\),然后算某个关键点到 \(u\) 的最段路和 \(v\) 到某个关键点的最段路,两边拼起来就可以直接贡献答案了。复杂度一只 \(log\)。
P7516
对于一张 \(n\) 个点 \(m\) 条边的有向图 \(G\)(顶点从 \(1 \sim n\) 编号),定义函数 \(f(u, G)\):
- 初始化返回值 \(cnt = 0\),图 \(G' = G\)。
- 从 \(1\) 至 \(n\) 按顺序枚举顶点 \(v\),如果当前的图 \(G'\) 中,从 \(u\) 到 \(v\) 与从 \(v\) 到 \(u\) 的路径都存在,则将 \(cnt + 1\),并在图 \(G'\) 中删去顶点 \(v\) 以及与它相关的边。
- 第 \(2\) 步结束后,返回值 \(cnt\) 即为函数值。
现在给定一张有向图 \(G\),请你求出 \(h(G) = f(1, G) + f(2, G) + \cdots + f(n, G)\) 的值。
更进一步地,记删除(按输入顺序给出的)第 \(1\) 到 \(i\) 条边后的图为 \(G_i\)(\(1 \le i \le m\)),请你求出所有 \(h(G_i)\) 的值。
solution1 \(O(n^3)\)
考虑在 \(f(j,G)\) 计算时,哪些 \(i\) 会对这个值造成贡献即使得这个函数中的 \(cnt\) 加一。分析可得条件是存在一个环使得环上的点编号大于等于 \(i\),并且 \(i \leq j\)。那么我们可以直接考虑每一对 \((i,j)\) 对答案(所有 \(G_i\))的贡献,令 \(f_{i,j}\) 表示 \(i\) 到 \(j\) 的路径上中间经过的点的最小值最大是多少,那么如果 \(\mathrm{min}f_{i,j},f_{j,i} > x\) 那么就可以贡献到 \(G_x\),这个转移可以先在边界处更新一下最后做一个后缀 \(\mathrm{min}\) 就行了。如何计算 \(f_{i,j}\)?考虑直接用 \(floyd\),因为 \(floyd\) 的过程就是一个一个加入中间可能经过的点,转移是简单的,懒得讲了。
solution2 \(O(\frac{n^3}{w})\)
考虑暴力加边,维护答案。我们令 \(f_{i,j},g_{i,j}\) 分别表示只走 \(\geq i\) 的点的情况下,\(i\) 能否走到 \(j\) 和 \(j\) 能否走到 \(i\)。那么对于新加入的一个 \((u,v)\),影响是十分有限的,直接 bitset 优化。(这里不想写多是因为我原本这个写完了的,结果不慎关机丢了,心情不太好)
CF1253F
有一个 \(n\) 个点 \(m\) 条边的无向连通带权图,有 \(k\) 个特殊点,你初始有一个电池电量为 \(c\),到特殊点就可以回满到 \(c\),经过一条边它的电池电量会剪掉边权,电量不能小于 \(0\)。有 \(q\) 个询问,每次给出 \(u,v\) 求出从 \(u\) 走到 \(v\) 初始的 \(c\) 的最小值。
\(n,k \leq 2 \times 10^5\)。
奇妙的题。
我们先考虑 \(u\) 到 \(v\) 到底是怎么走的,显然是长成不停的赶往下一个充电站然后最终到达 \(v\) 的样子。那么如果我们把每两个到达的加油站中间走的路径分别拆出来,把这些路径的长度取 \(max\) 就是初始的 \(c_{min}\) 了。现在要做的就是合理的走这些路径。
我们考虑 \(u\) 到 \(v\) 的这条路径上的一条边 \((x,y)\),那么它所在的路径长度至少为 \(d_x+w_{x,y}+d_y\),其中 \(d_x,d_y\) 表示 \(x,y\) 到最近的特殊点的距离。那么对于从 \(u\) 走到 \(v\) 的一条路径(整体的,不是拆过的),此时的 \(c_{min}=max_{(x,y) \in path}{d_x+w_{x,y}+d_y}\),是能取到这个下界的,总可以调整到一个方案。所以可以跑完 \(d\) 数组以后把每条边边权变成上面的三个东西相加,求 \(u\) 到 \(v\) 的路径 \(max\) 最小的路径。这个用 kruskal 重构树即可。
P4899
题意见锣鼓。
可以发现就是你得先走 \(\geq L\) 的,然后再走 \(\leq R\) 的。那么其实就是要满足起点出发走 \(\geq L\) 能到达的点集,与终点出发走 \(leq R\) 的点集合有交。那么考虑建立两棵 kruskal 重构树,分别是把每一条边边权置成 \(\mathrm{max}(u,v)\) 和 \(\mathrm{min}(u,v)\) 的情况下得到的。那么容易发现开始所说的两个点集就是 \(T_1,T_2\) 的两个子树,于是离线树状数组即可解决。
CF1515F
给定一个无带权向图,每个点有点权 \(a_i\),你每次可以选择两个点 \((i,j)\) 满足 \(a_i+a_j \geq x\) 让 \(i,j\) 连通。连通后的点之间可以任意转移 \(a\) 的值。问能否让所有点连通,如果可以构造方案。
首先若 \(a\) 之和小于 \((n-1)x\) 那么就寄了。否则的话考虑任意取一棵生成树,考虑剥叶子,如果叶子 \(\geq x\),那么就直接连上,否则放后面连。可以这样一定能做完。
CF1633E
给定一个无向带权图,有 \(q\) 个询问,每次给定 \(x\) 表示将所有边的边权变成 \(|w-x|\),求此时 mst,询问独立。
solution1
考虑 kruskal 的过程,我们只关心所有边的边权的相对大小关系,观察到所有 \(x\) 使得边权关系本质不同的只有 \(O(m^2)\) 种,所以直接对每一种跑一次 mst 即可。
solution2 (P9531 [JOIST 2022] 复兴计划)
考察 solution1 的过程,可以发现在 \(x\) 不断移动的过程中,在某个时刻的答案变化,一定是有一些边进来,一些边出去。并且只要一条边出去了,那么它的边权就会不断变大,那么后面就更不可能在加入答案了,因此每个边要么没有出现,要么在一段 \(x\) 内出现。如果我们能求出每条边出现的区间,那么问题就好做很多了。
考虑分治(怎么这几天见到这么多用分治处理区间的东西 qwq),对于当前的分治区间的中点 \(x=mid\) 求出 MST,那么不在 \(mst\) 上的边显然对应的区间要么被 \([l,mid]\) 包含要么被 \([mid+1,r]\) 包含,至于被哪一个包含只需要看这条边边权最小的时候 \(x\) 在 \([l,mid]\) 还是 \([mid+1,r]\) 即可。对于在 \(mst\) 上的边,意味着它在 \([l,mid]\) 和 \([mid+1,r]\) 都有,如果直接递归下去复杂度可能会炸,然而我们发现一件事情,假设在递归下去的某个区间的 \(mid\) 第二次包含了这一条在 \([l,r]\) 的中点 mst 出现了的边,那么对于这个区间 \([l',r']\) 的其中一半区间就一定是都满足它在的,至于是哪一半就看 \([l',ri]\) 在 \([l,mid]\) 还是 \([mid+1,r]\) 的内部。
复杂度分析:
注意到这个东西是一个类似于线段树的结构,所以是 \(O(m\mathrm{log}^2 m+q\mathrm{log}\ m)\) 的。
solution3 (P9531 [JOIST 2022] 复兴计划)
因为此做法用到了 动态树,所以我得咕咕咕。
P7831
给定一张有向图,每条边有两个两个属性 \((r,p)\),表示经过这条边你必须携带 \(r\) 元,经过之后可以获得 \(p\) 元。对于每个点求出从它出发初始最少携带多少元,才能无限游走。
\(n,m \leq 2 \times 10^5\)。
首先肯定是得 \(dp\) 的,令 \(f_i\) 表示 \(i\) 的答案,初始都为 \(\mathrm{inf}\)。
显然不能直接转移,因为这个转移带环。对于这种转移带环的,肯定是得发掘一些性质拆环。
然后可以发现一些性质:
- 出度 = 0 的点可以删掉,显然它的答案不会再变了。
- 对于当前还存在的边里面 \(r\) 最大的那条 \((x,y,r,p)\),它只能说明 \(f_x \leq r\),更新完以后也可以删掉。
因此可以发现这两种配合起来就能删完整个图把答案更新完了。
对于第一种,假设这个要删掉的点是 \(x\)。那么对于它的所有入边 \(y \to x\),有更新 \(f_y \leq \mathrm{max}(r,f_x-p)\)。
对于第二种,就直接更新 \(f_x \leq r\) 即可。
CF1062F
咕咕咕。
CF1994F
给定一张有向图,有一些边必须经过,求一个环使得它经过所有必经边。
\(n,m \leq 2 \times 10^5\)。
问题可以转化成删掉一些不必经的边使得图存在欧拉回路,所以直接 dfs 树上贪心一下就行。
Metric-TSP
给定无向图 \(G\),每条边有边权 \(d(u,v)\),求边权和最小的哈密顿回路,保证满足三角形不等式 \(d(u,v)+d(v,w) \geq d(u,w)\),不妨默认 \(G\) 是完全图。
求 \(2\)-近似解,\(\frac{3}{2}\)-近似解。其中 \(k\)-近似解的含义是求出来的近似解的答案不超过准确答案的 \(k\) 倍。(说句闲话,xzy 讲课的时候把 \(\frac{3}{2}\) 念成 \(\frac{2}{3}\) 了,比较变态 ❤️)
\(2\)-近似解(Tree doubling algorithm)
假设答案是 \(minv\)。
找到最小生成树,假设 mst 的边权和是 \(T \leq minv\),复制一遍(对于树上所有边加一条重边),跑欧拉回路,显然此时的权值不超过 \(2T \leq 2minv\),记录每个点出现的第一次就可以认为是哈密顿的经过的顺序,然后把过程中重复经过的缩一下根据三角形不等式显然是不会让权增大的。
我们记这种为了把重复经过的点弄掉而把一段路径缩成一条边的整体操作称为 shortcutting,之后 shortcutting 就代表此行为。
\(\frac{3}{2}\)-近似解(Christofides algorithm)
同样先找到 mst 称为 \(T\),然后对于上面度数为奇数的点的导出子图,找出它的最小权匹配 \(M\),并构造 \(T+M\) 找出欧拉回路然后 shortcutting。
如何证明答案呢?我去我怎么不会。
P9697
直接看锣鼓的。
因为后面的操作会覆盖前面的,所以考虑时光倒流,这样的话相当于操作的时候的如果之前覆盖过就不动了。那么对于 \(x=y=1\) 的操作显然是放末尾,\(x=y=2\) 的放开头。现在要考虑的就只有 \(x,y=1,2\) 的情况了。假设 \(x=1,y=2\),如果不是可以直接 swap 两个位置。那么考虑从 \(l\) 连向 \(r\)。然后我们发现一件事情,如果我们操作了一个 \(x \to y\) 的边,那么 \(x\) 能到达的其它的所有点都可以变成 \(2\)。因此我们考虑缩点,那么对于入度不为 \(0\) 的 scc 就可以直接全变成 \(2\),入度为 \(0\) 的 scc 最多只会把一个涂成 \(1\)。如果这个入读为 \(0\) 的 scc 之前有位置被涂成过 \(2\),那么就不用把一个涂成 \(2\) 以达到效果了。
CTT2023 Day4 T2 阉割版
定义两张边集一样的图等价当且仅当:对于所有边集来说,两个图的最小生成森林形态一样。给定一张图,满足边权是排列,求有多少个图形态不变但是边权是原图排列的图和原图等价。\(n,m \leq 10^5\)。
根据 破圈法(最小生成树算法),得到两图等价当且仅当每个简单环的边权最大的那条边相等。证明不难。如何判定两条边在一个简单环内?根据门杰定理,当且仅当它们属于同一个点双。因此我们可以考虑对于每个点双分别计算答案然后归并。归并的系数是什么呢?对于不同点双内部的边,它们之间大小关系是不重要的,我们只需要保证点双内的边的大小关系,因此其实我们只需要给每个点双内部分配一些边权即可,所以还得乘上这么一个分配的组合数。
我们先假设把上面的组合数乘上去了,也就是说每个点双内部的边权都分配好了,那么现在就是要对于一个点双内部来看有多少个排列满足要求了。
现在就只需要考虑一个点双了,那么首先边权最大的那条边,这个边的权显然得是固定的,所以我们可以直接删掉这条边然后继续划分成若干双连通分量,这是个子问题也可以归并上来答案。但是删边是不好做的,我们考虑时光倒流变成加边,并在过程中维护 v-dcc 的结构。我们考虑取图的最小生成树,那么加入其余边的一条边,比方说是 \((u,v)\) 的影响其实就是把 \(u\) 到 \(v\) 的路径上的边缩到一个点双内,可以直接用并查集维护每个点的 top 节点,暴力往上跳然后合并即可。
P8456
咕咕咕。
动态 scc
动态加边维护 scc 信息,可以离线。\(n \leq 10^5\)。
比如说你需要维护所有 scc 大小的平方和。
我们考虑求出每条边的两个端点在什么时刻第一次被缩进一个 scc,这样我们就可以直接当成无向图维护连通分量直接用 Dsu 维护即可。
考虑整体二分,我们先把 \([l,mid]\) 的边拿出来跑 tarjan,然后对于 \([l,r]\) 的边,如果它在 \([l,mid]\) 并且在 \([l,mid]\) 跑完之后在一个 scc 内就丢到左边,否则丢到右边即可。
CFgym104128J
给定一个长度为 \(n\) 的数组 \(a\),若 \(|i=j|=|a_i-a_j|\) 则在 \((i,j)\) 间有一条边,问是否存在完美匹配,若存在则构造任意匹配方案。\(n \leq 10^5\)。
容易发现 \(|i=j|=|a_i-a_j|\) 即为 \(i+a_i=j+a_j\) 或 \(i-a_i=j-a_j\)。那么我们考虑连上 \((i-a_i,i+a_i)\) 这个边,那么 \((i,j)\) 有边当且仅当它们对应的边在图上有公共点。所以这就是个边的完美匹配问题。
先随便找一棵 dfs 树,然后从深到浅考虑每一个点。找到所有和它相连的未被匹配的边,除了它连向父亲的边(这条边显然未被匹配)。如果这些边是偶数条,两两匹配即可,连向父亲的边会在处理父亲时被匹配上。如果这些边是奇数条,就把连向父亲的边也加入匹配。
这个构造方式唯一可能出问题的地方,在于根节点不存在连向父亲的边。但考虑到构造过程容易发现,当我们递归回到根节点时,此时 dfs 树上未匹配的边都是从根节点连向子节点的边(这里的子节点是直接子节点,而不是子树中的节点)。由于之前处理每个点时都让每两条边互相匹配了,如果此时未被匹配的边有奇数条,说明整个连通块边的总数也是奇数条,不符合之前的假设。因此这个构造方式一定能构造出可行解。
CF858F 就是边匹配板子。

浙公网安备 33010602011771号