Cry_For_theMoon  

换种方式来写。

XXII Open Cup,Korea:

A. Automatic Sprayer 2

这个构造场上过的不少,但是真实难度并不低。

考虑如果我们能解出每一行,每一列的和 \(r/c\)。那么根据一定有解这个事实,我们一定能构造出一个合法的矩阵,考虑以下的网络流模型:

建立二分图,左行右列。然后 \(s\) 连向所有行容量 \(r\),所有列连向 \(t\) 容量 \(c\)。任意行连向任意列容量均为 \(\infty\)

则一组最大流就对应了一个矩阵的方案。显然贪心地流即可,换言之:

for i in [1,n] : 
	rest = r[i]
	for j in [1,m] : 
		ans[i][j] = min(rest,c[j])
		c[j] -= ans[i][j],rest -= ans[i][j]

总可以构造出合法解。

现在压力来到如何求 \(r/c\)

一个错误的方向是:尝试变成一维的情况然后求解。但是如果仔细思考以下,会发现 \(r,c\) 根本就不能独立变成一维情况。

考虑到我们拥有方程: \(b_{x,y} = \sum_{i}r_i\times |x-i| + c_i\times |y-i|\)。还有方程 \(\sum r = \sum c\)。结合起来的话,我们暴力高斯消元肯定是能解出 \(r,c\) 的,问题就是不够快。因此考虑利用前面这部分方程来快速消元。

注意到 \(b_{x,y}\)\(b_{x+1,y}\) 的话,\(c_i\) 这部分的贡献完全一致,而考察 \(r_i\) 的贡献,形如 \(,...2,1,0,1,2,...,\)。事实上如果我们把 \(b_{x,y}\) 减去 \(b_{x+1,y}\),会发现得到的 \(r_i\) 贡献的序列长成 \(-1,,...,-1,1,...,1\) 这样。而如果我们再把 \(b_{x-1,y}\)\(b_{x,y}\) 减去,那么这两个序列只在 \(r_x\) 的贡献上有差异:前者系数为 \(-1\),后者系数为 \(1\)

因此我们两个差相减后再除二就得到了 \(r_x\)。形式化地:\(r_x = \frac{(b_{x,y}-b_{x+1,y}) - (b_{x-1,y}-b_{x,y})}{-2}\),这里 \(y\) 可以任取。类似我们能解出 \(c_y\)

但是发现一个很关键的问题:就是我们要用到 \(b_{x-1}\)\(b_{x+1}\)。所以事实上我们只能算出 \(r_{2\sim n-1}\),还有 \(c_{2\sim n-1}\)。对于 \(r_1,r_n,c_1,c_n\) 我们还需要其它的计算方式。

由于这个时候只有四个未知数了,也许直接跑高斯消元就可以了,但这样也太麻烦了。我们考虑找到一个四元方程组然后取之。观察 \(n^2\) 个元素对应的方程组:发现大部分方程里四个未知量都出现。但是有时候,比如 \(x=1/n\) 或者 \(y=1/n\) 的时候,就会少出现一些未知量。比如 \(b_{n,n}\) 的方程里没有 \(r_n\)\(c_n\)。那我们就可以得到一个二元方程组:\(k_1 r_1 + k_2 c_1 = b\)。事实上注意到 \((n-1)(r_1+c_1) = b_{n,n} - \sum_{i=2}^{n-1}(r_i+c_i)\times (n-i)\),所以我们直接得到了 \(r_1\)\(c_1\) 的和。

类似地其实 \(r_1+c_n\)\(r_n+c_1\),还有 \(r_n+c_n\) 的和都可以求出。问题是这四个方程不是线性无关的。此时用上 \(\sum r = \sum c\) 作为第四个方程即可。这里需要手动模拟一下消元,但会比最开始暴力跑高斯消元方便很多(那样的话矩阵元素还会变成实数)。

这样我们就在 \(O(n^2)\) 的时间内解决了本题。

代码

B. Cilantro

这道题没有那么难想,但是思维含量也并不低,场上的数据也印证了这点。

首先发现一件事情:只要 \(a\) 序列和 \(b\) 序列的 \(1\) 个数相等,则我们一定能找到一种 \(a\) 的出入栈方式使得其等于 \(b\)。构造方式也非常容易,我们贪心扫描即可,每次先尝试取出栈顶元素,如果不行,再考虑当前的 \(a_i\),然后决定是压入栈中还是直接弹出即可。

既然这样,我们能让 \(a_i\) 第一个被弹出的话,一个必要条件是 \(a_i = b_1\)。然后我们考虑 \(a_{1},a_{2},...,a_{i-1}\) 此时是依次位于栈中的。而后面 \(\gt i\) 的部分可以拼出任意序列的话。此时有一个错误的想法:就是我们尝试在 \(b[2,n]\) 中找到一个 \(a_{i-1},a_{i-2},...,a_{1}\) 的子序列,则有解等价于能找到这样一个子序列。

但这实质上有问题,因为我们并不能完全自由的安排 \(a_{i-1},a_{i-2},...,a_{1}\) 的出现:假如我们在一个时刻想要把 \(a_{i-1}\) 扔出来,那 \(\gt i\) 的元素就不能有人留在栈中,对于更前面的元素也是同理的。

如果我们能让 \(a\)\(\gt i\) 的部分的 \([x,y]\) 段和 \(b\) 中的 \([l,r]\) 匹配,那么我们就可以从 \(\lt i\) 的那些 \(a\) 里抽一个连续段出来,依次和 \(b_{r+1},b_{r+2},...,\) 去匹配,显然我们也只能在这些时刻把 \(a_{1},a_{2},...,a_{i-1}\) 拉出去和 \(b\) 匹配。

因此考虑这样一个贪心:我们从小到大枚举 \(i\) 的时候,维护两个指针 \(l,r\),初始 \(l=r=n\)。当我们的 \(i\)\(j\) 变到 \(k\) 的时候,我们就需要把 \(a[j,k)\) 依次和 \(\lt r\) 的位置匹配上。所以我们依次考虑 \(a_j,a_{j+1},...,a_{k-1}\) 的匹配位置。如果 \(a_j = b_{r}\),那么我们直接把两者匹配上就行。否则 \(b_{r}\) 就是去和 \(a_l\) 匹配的:我们不断向前同时缩小 \(l,r\),直到 \(a\) 这部分的 \(1\)\(b\) 这部分的 \(1\) 个数相同即可。然后这个时候我们重新尝试把 \(a_j\)\(a_{r-1}\) 匹配,如果还是不行,那就继续缩减 \(l,r\)。直到说 \(l\lt i\) 或者 \(r=i\) 了为止。

这样我们就在 \(O(n)\) 的时间内解决了这个问题。

代码

E. Goose Coins

算是一道中规中矩的题。

看上去这么恐怖的背包一定是需要挖掘性质:注意 \(c_i\) 总是 \(c_{i-1}\) 的倍数,这启发我们把状态看作一个数,第 \(i\) 位(其中 \(i\lt n\) )始终不超过 \(\lt c_{i+1}/c_{i})\),然后最高位可以取任意值。

这样我们得到的实质上是花费硬币最小的解:考虑利用数位 dp 的思想,从高维开始把一些大数位(也就是面值大的硬币)分解为更小的硬币向下推。

只考虑最大值,显然最小值没有区别。令 \(dp(i,x,y)\) 表示考虑了 \(\gt i\) 的数位,然后一共选了 \(x\) 个硬币,我们向第 \(i\) 位传递了 \(y\) 枚硬币过去,此时的最大答案。那么暴力枚举转移,也就是枚举第 \(i\) 位保留了多少枚硬币,这是 \(O(k)\) 的。这样这个 dp 就是 \(O(nk^3)\) 显然无法通过。

当然这其实犯蠢了,我们转移的时候直接讨论是否保留至少一个硬币,如果是,则转移到 \(dp(i,x+1,y-1)\),否则直接全部扔给 \(i-1\) 就行了。也就是在做类似完全背包的那个复杂度优化。这样时间复杂度 \(O(nk^2)\) 可以通过。

代码

G. Lamb's Respite

最开始没有过的时候觉得它很恶心,最后发现其实是存在很好的实现方法的。

首先有经验的话会发现这个题目非常像 IOI2021 分糖果这道题,区别是这里碰到 \(0\) 就直接死了,那么感觉会方便一些。很显然分成三段来讨论:中间那段是锁血的,那我等价于判断是不是有一次碰到了这个界,如果是的话直到锁血结束血量都是 \(\lceil \frac{H}{10} \rceil\),和第一/三段里碰到 \(0\) 就死是类似的。

先来考察第一段里如何判断是否碰到 \(0\):发现等价于有一个连续段的 \(\sum a\)\(\le -H\) 的,必要性和充分性都很好说明,那我们求的就是区间最小子段和。如果没有碰到过 \(0\):考虑全局最大前缀和所在的位置 \(p\)(我们把时刻 \(0\) 也考虑进去),如果有多个就考虑后面那个。显然时刻 \(p\) 我们的血量 \(=H\) 且后续不再有顶到 \(H\) 的情况。因此 \(sum-sum_{p}\) 就是血量的下降值(这里 \(sum\)\(a\) 的前缀和)。所以我们还要支持求最大前缀和的值。

发现后面两段有点小问题,就是初始生命值不是 \(H\) 了:这里有一步很妙的事情,就是我们不妨认为初始生命值是 \(H\),然后在开始前额外遭受了一次攻击。这样我们就不需要再去考虑任何多的事情了!

上述的信息都可以直接用线段树维护,时间复杂度 \(O(n+q\log n)\)

代码

K. Three Competitions

场上差一点通过了,有点可惜。

首先会发现根据抽屉原理任意两个人之间总有一个人能击败另一个人,所以这是个竞赛图。

竞赛图两点可达性,考虑利用其缩点后是链的性质,只要能快速缩点即可。

根据兰道定理,按照出度升序排序后,所有 \(sum_i = \dbinom{i}{2}\) 的位置划分开,就对应了链上的一个节点。

所以问题变成求每个点的出度。

场上有点急,写了个 bitset 没过。冷静一下我们发现其实就是三次二维数点减去一次三维数点就好了。时间复杂度 \(O(n\log^2n + q)\),其实这种情况的三维数点能 \(O(n\log n)\) 但意义不大。

代码

CCPC Online 2023

C. Clique Challenge

很神的题!第一次知道可以解决 \(m\le 1000\) 的无向图最大团和团数目(最大团数目也可以)的算法,并且没有用到任何科技。

考虑把点按照度数排序然后重标号。则一个人向标号更大的点最多只有 \(\sqrt{2m}\) 个点连边。

那么我们枚举标号最小的点,这样就只有 \(v=\sqrt{2m}\) 个点和它有连边,我们考虑这 \(v\) 个点的团数目即可。

注意到 \(v\le 44\),考虑折半。那么假设左边的点集是 \(S\),右边的是 \(T\)。我们枚举 \(S\) 后可以求出 \(X\) 表示说右边的点,\(X\) 内的点和 \(S\) 的每个点都有连边。那么我们就只用知道 \(X\) 子集内有多少个合法的 \(T\),直接高维前缀和一下即可。令 \(v:=\frac{v}{2}\) 我们就在 \(O(v2^{v})\) 的时间内解决了这个问题,常数很小。

最坏情况下有 \(\sqrt m\)\(v=\sqrt{2m}\) 的点,所以这个算法的最坏复杂度是 \(O(m\times 2^{\sqrt{2m}})\) 的,但是远远跑不满,且常数非常小。事实上还存在 \(O(2^v)\) 解决一个点的情况的算法?所以复杂度能做到更优来着。

记录

G.GCD of Pattern Matching

如果不是场上过了非常多我就放弃了,最后才做出来(

先考虑我们枚举一个 \(p\) 然后检查是否能让所有数都是 \(p\) 的倍数。

考虑如果我们确定了使用哪些数码,那我们随意找一个方式填进去,然后我们发现用交换操作能得到所有这个数码构成的数。则交换前和交换后都要是 \(p\) 的倍数,那你考虑假设我们交换前的位权和分别是 \(a,b\)。然后数字差为 \(c\),那么就要求 \((a-b)\times c\)\(p\) 的倍数。事实上我们总能让 \(c=1\),所以就要求 \((a-b)\)\(p\) 的倍数。

那么我们把初始的那个值和所有 \(a-b\) 去取 \(\gcd\) 就好了。

不过有点小问题:注意到最高位不能为 \(0\),所以当 \(m=2\) 的时候最高位那个类只能填 \(1\),没有人能和他换。

然后 \(\Sigma = m\) 的情况就做完了,因为字符集是确定的,但是 \(\Sigma \lt m\) 的时候怎么办?

alpha 提出了一个转化:我们搞一个中转站,初始的时候我们随便选 \(\Sigma\) 个数码填好,把剩下的 \(m-\Sigma\) 个数码扔进中转站。然后我们除了互换两个等价类的数码以外,还多了一个操作:选择一个等价类,然后把中转站的某个数码换出来,再把本来的数码换进中转站。如果位权和为 \(a\),且数字差为 \(c\),则我们就需要让 \(a\times c\)\(p\) 的倍数。类似地我们总能让 \(c=1\),除了 \(m=2\) 的时候最高位的等价类固定为 \(1\)。因此我们只需要把答案再和每个位置的位权和取 \(\gcd\) 即可。

这样的话有 \(O(\Sigma^2)\) 次取 \(\gcd\) 操作:唯一瓶颈在于第一部分里,我们对于两两等价类的位权和的差,把他们求 \(gcd\)。也就是有 \(a_1,a_2,...,a_k\),要求两两差的 \(\gcd\)。其实我们求 \(a_1\) 和其他人的 \(\gcd\) 就好了,这样只有 \(O(\Sigma)\) 次求 \(\gcd\) 操作,即可通过。

代码

H. Hurricane

过的反而比 G 少很多,真是奇妙。

容易想到:枚举一个点 \(u\),考虑其邻点集合 \(S\) 以外的点,到他的距离都是 \(1\)

那么对于一个 \(v\in S\),什么时候 \(dis(u,v)=2\)?当且仅当存在一个点 \(w\) 使得 \((u,w)\)\((v,w)\) 之间都没有连边。

发现如果说 \(dis(u,v) \gt 2\),那么意味着两个人里有一个人的 \(deg\) 要大于等于 \(\lceil \frac{n}{2} \rceil\)。而这样的点只有 \(O(\frac{m}{n}) = O(\sqrt m)\) 个。

因此我们对这些点特殊处理,跑一下 bfs 即可,可以用并查集做到 \(O((n+m)\log)\),当然这个 \(\log\) 非常小,其实补图最短路还有严格的 \(O(n+m)\) 做法,因此这个题可以做到 \(O((n+m)\sqrt m)\) 的复杂度。

代码

XXI Open Cup,Tokyo

这场预估会写很多题来着,先写几个已经过的。

E. Edge Subsets

好题!

考虑我们可以建立一个网格图:对于 \(v\in [0,n)\),我们把他放入第 \(\lfloor \frac{v}{b} \rfloor+1\) 行。至于列就按照 \(v\bmod b\) 顺序。

则我们会发现:连边只会出现在:两个在同一列中相邻的点(对应了距离为 \(b\) 的边),或者两个在同一行/相差一行的距离为 \(a\) 的点。 因此我们可以设计出一个 \(O(n2^{b})\) 的 dp:直接按照行的顺序,从上往下从左往右考虑每个格子即可。

\(b\lt 20\) 的时候执行这个就可以了,否则我们注意到网格图的列长度不超过 \(10\) ,再设计一个 dp。注意到一个问题:就是我们会出现,一行的末尾,和下一行的开头连边的情况,而且每一行还会有 \(a\) 个这样的情况发生,换言之我们按列 dp 的时候貌似会出问题。

这个时候我们考虑重新安排列的分配,使得这张网格图的性质更好:我们知道 \(ka\bmod b\) 构成 \(\gcd\) 个置换环,那就把一个环接一个环地放上去:也就是先把 \(0,a,2a,...,\) 放上去,然后把 \(1,1+a,1+2a,...,\) 放上去。那么整个网格图会被分成 \(\gcd\) 个独立的部分,每个部分都有 \(\frac{b}{\gcd}\) 列。我们对每个部分独立 dp 即可:注意到此时考察 \(v-u=a\) 的边 \((u,v)\),则 \(v\)\(u\) 的列一定相差 \(1\),因此有可能是 \(u\) 在第 \(b\) 列而 \(v\) 在第 \(1\) 列,且要么在同一行要么 \(v\)\(u\) 的下一行。此时这张网格图的性质就很好:我们依旧是类似第一部分的 dp,改成按列 dp,但是需要额外记录第一列的所有状态,以便最后一列转移的时候使用。这样复杂度是 \(O(n4^{\frac{n}{b}})\) 的。

平衡一下,我们就得到了一个 \(O(n2^{\sqrt{2n}})\) 的做法。

代码

F. Find the LCA

先考虑一个简单一点的问题:就是 \(x=1\) 有多少种可能?

这个问题看上去非常复杂,我们打一个简单的表会发现一个规律:\(n=2\) 的时候 \(ans=1\)\(n\ge 3\) 开始 \(ans=\frac{(n-1)!}{2}\)。我认为这也许是发觉这一点的唯一方式。。。

但发觉以后的证明没有那么玄妙:我们考虑双射,也就是每一种 \(lca(n-1,n)=1\) 的和每一种 \(lca(n-1,n)\neq 1\) 的形成一一对应。

先考虑前者:我们假设 \(n-1\) 是属于 \(1\) 的儿子 \(x\) 所在的子树。则我们把 \(x\) 这整颗子树拿出来。然后在 \(1\rightarrow n\) 的路径上(显然编号递增),找到一条边 \((u,v)\) 使得 \(u\lt x\lt v\),显然这样的边存在且唯一。然后 \(x\) 放在这条边中间,\(x\) 的子树也跟着接过来即可。这样,每一个 \(lca(n-1,n)=1\) 的情况都唯一映射到一个 \(lca(n-1,n)\neq 1\) 的情况。

再来考虑后者,我们找到 \(x=lca(n-1,n)\),显然 \(x\neq n\),那么我们令 \(n\) 是属于 \(x\) 的儿子 \(y\) 所在的子树,我们把 \(x\) 挂到 \(1\) 上,再把 \(x\) 除了 \(y\) 以外的子树都跟着接过去,然后重新连接原来的 \(fa_x\)\(y\)。这样,我们反过去也形成了对应。

当这部分算完以后,整个问题变得简单很多:我们枚举一个集合 \(S\) 表示说 \(x\) 子树的集合为 \(S\),然后思考这有多少种可能。

我们先特判 \(|S|=2\) 的情况(其实也就是 \(S=\{n-1,n\}\)),然后一定有 \(|S|\ge 3\)。此时我们的方案数分为几部分:

  • \(|S|\) 内部的点的确定。
  • \(|S|\) 外部的点的确定。
  • \(|S|\) 的根(也就是 \(\min S\) 会挂在外部的那个位置)。

第一个部分的答案是 \(\frac{(|S|-1)!}{2}\),第二个问题的答案是 \((n-|S|-1)!\),第三个问题的答案是 :如果 \(\min S=1\) 则答案为 \(1\),否则答案为 \((\min S-1)\)。所以我们特判 \(\min S=1\) 也就是 \(S=\{1,2,...,n\}\) 这种情况即可。

注意到还有 \(a_i\) 这个权的限制。考虑如果没有 \(min(S)-1\) 这项,我们直接对每个 \(i\) 算出所有 \(|S|=i\) 的情况的 \(\prod_{i\in S} A_i\) 的和即可,这是容易分治 NTT 解决的。

现在加上 \(\min S-1\) 这项,依旧可以用分治 NTT 解决,同时维护 \(\prod A_i\) 的和,还有 \((\min S-1)\times\prod A_i\) 的和就好了。时间复杂度 \(O(n\log^2 n)\)

代码

H. Harsh Comments

这个题第一眼给人的感觉就是 \(\min-\max\) 容斥,然而注意到很不寻常的点是:\(a_i\le 100\) 但是 \(b_i\lt 998244353\)。因此常规的背包做法就被否决掉了,而且很可能是一个对 \(a\) 做背包但是不对 \(b\) 做背包的做法。

那我们先不考虑暂时毫无帮助的 \(\min-\max\) 容斥做法,来考虑 \(n=1\) 怎么做(因为 \(n=1\) 的时候\(\min-\max\) 容斥已经毫无意义了):我们换个形式求和,不妨枚举 \(B\) 序列的一个人 \(j\),考虑他有多大的概率先于 \(A_1\) 被删除。

那么这个概率其实和 \(A_1,B_j\) 以外的人毫无关系,其概率就是 \(\frac{b_j}{a_1+b_j}\)

现在我们可以回到 \(\min-\max\) 容斥了:对于一个 \(A\) 的子集 \(S\)\(E(\min S)\) 的意义就是说 \(S\) 集合里最早被删除的帖子期望是什么时候被删的。那么我们不妨计算期望有多少个 \(S\) 以外的人早于 \(S\) 被删。然后 \(+1\) 就是想要的。因为我们全部 \(-1\) 了(不管是 \(\min\) 还是 \(\max\) 的定义),所以我们把 \(\min\) 减一算,最后算出的答案加一就可以。

那么这有分为两部分:

  • \(A\)\(S\) 以外的对 \(S\) 的影响。
  • \(B\)\(S\) 的影响。

事实上第一部分经过一些推导可以发现对所有 \(S\) 求和后就是 \(n-1\)(直接算也是可以的)。对于第二部分:我们不妨枚举一个 \(B_j\) 然后考虑他对所有 \(S\) 造成的贡献和。那我们发现只和 \(S\) 内的 \(\sum a\) 有关,这不是伏笔回收了吗!做个背包就好了,时间复杂度 \(O(n\sum a)\),取之。

代码

I. Inverse Problem

首先需要注意到一个关键性质:我们令 \(i\) 是给出的序列的最长递增前缀长度。则 \((i,m]\) 这一段中间不能塞入任何其它数。(但我们可以在 \(b_{i}\)\(b_{i+1}\) 中间塞)。

因为假设我们在这样的一个位置赛数了,我们就总能把 \(b_i\) 换成 \(b_{i+1}\) 然后得到一个新的长度为 \(m\) 的序列。

现在我们就考虑给出的 \(b\) 是升序的怎么做。

此时情况就变得很简单:我们把问题看成是不在 \(b\) 的数插进这个序列里。如果一个人插在了 \(b_{i}\)\(b_{i+1}\) 之间,它应该 \(\gt b_{i+1}\)(如果插在 \(b_m\) 后面那就应该 \(\gt b_m\))。所以我们从小到大插数即可。

时间复杂度 \(O(n)\)

代码

SNOI 2019

有时间尝试再做一下最后一题。

B. 数论

和之前的一道题 CF516E 比较类似,但是简单了很多。

考虑同样的模型:我们不妨假设 \(p\le q\)。则我们考察 \(\bmod p=i\) 的人,第一次是 \(i\),第二次是 \(i+p\),第三次是 \(i+2p\),第四次是 \(i+3p\)... 那么他的取值形如 \(i+kp\)。我们知道这个东西 \(\bmod q\) 意义下,构成 \(\gcd(p,q)\) 个环。

我们直接枚举 \(i\in [0,\gcd)\),然后建出 \(i\rightarrow i+p\pmod{q}\rightarrow i+2p\pmod{q}\rightarrow ... \rightarrow i\) 这样一个环。则对于每个 \(x\equiv i\pmod{\gcd}\)(且 \(0\le x\lt p\))而言,它初始位于环上值为 \(x\) 的位置。然后每过 \(p\) 次就走到下一个位置。若 \(x\)\(A\) 集合内,且 \(x\) 所在的那个位置在 \(B\) 集合内,就会对答案有 \(1\) 的贡献。

我们枚举 \(x\),则显然问题转化成了:给一个环,有些点是特殊的。在环上某个点出发走若干步,询问一共经过了多少次特殊点。维护环上前缀和以后容易 \(O(1)\) 计算。这样整个问题就在 \(O(\max\{p,q\})\) 的时间内解决了。

代码

C. 通信

没有发现一些比较好的贪心性质或者 dp 方式,所以考虑尝试用 flow 来解决。

那么直接就有一个很朴素的费用流做法:我们对每个点拆成两个点 \(in\)\(out\)。然后 \(out\)\(T\) 连边 \(c=1,w=0\)\(S\)\(out\) 连边 \(c=1,w=W\),这就是第一种连接方式;对于第二种,我们考虑 \(S\)\(in\) 连边 \(c=\infty,w=0\),然后 \(in_i\) 向后面的 \(out_j\) 连边 \(c=1,w=|a_i-a_j|\)。这个做法可以获得 \(80\) 分。

考虑优化一下边数,这个 \(n^2\) 显然可以用一些优化建图的手段缩减。

如果没有 \(i\lt j\) 的限制,考虑按照 \(a_i\) 把所有点排序,然后做个前后缀虚点优化一下建图就好了,这样边数都 \(O(n)\) 了。

现在带上 \(i\lt j\) 的限制就套一下 cdq 分治的手法,这样边数就是 \(O(n\log n)\) 了。此时费用流就足够通过。

代码

E. 积木

感觉是这一场六道里思维最高的题。

首先想到一个贪心:我们考虑当前的空格 \((x,y)\):如果在最终局面里这个位置不是空格,那么它一定被上/下/左/右中的某个位置的积木覆盖,所以我们直接把空格放到那个位置的积木里去即可。这个过程里,归位的积木永远在变多,所以一定不会出现死循环,有限步数后我们会让空格位于目标位置。

但这样会出现一些问题:就是说可能还有一些积木的位置不对。

我们必须换一个视角来看这个问题:我们把两个局面做一下异或:考虑那些只在第一个局面出现的边,称为黑边;只在第二个局面出现的边,称为红边。首先我们会发现:整个图会被拆成若干个不相交环,且环上的边永远是红黑交替,然后我们考虑按顺序消去这些环。事实上如果回到积木的视角来看,我们就是要选中一个环然后把积木全部循环移位一格。

那显然空格不能和这个环相离,不然都无法操作:发现如果空格和环相邻,那么我们第一步把空格换进这个环里,这样有一个积木就有一头伸出去了,然后这个时候按顺序把剩下的环上的积木都循环移位一格,最后再把第一步换出去的积木转回来。这样我们在任何变动都没有发生的情况下把这个环成功的循环移位了一次。

所以考虑这样一个 dfs 过程:我们从起点(这个起点应该是,目标局面里空格的位置)开始 dfs,每个位置只被访问一次。然后枚举当前空格和邻接的哪个积木交换:如果这个积木在一个环里,我们就先进行上述的消环过程。然后把空格和这个积木互换,继续 dfs 下去。dfs 回来的时候,撤销互换。这样就在 \(O(nm)\) 的次数内完成了归位。

所以发现任何两个合法的状态之间都能完成互换。

事实上这个题在实现上也很有讲究,代码可以写的很精简。是一道非常好的构造。

代码

SNOI2020

如果 NOIP 结束后还没退役就找时间把这场剩下几个题补了。

B. 取石子

这个博弈题需要一些前置知识,如果了解的话反而没有那么难。

首先,我们注意到说这个“取完的人输”比较诈骗,因为最后一步的那个人一定是只拿了一颗石子,不然他就能少拿一颗让对面输。所以没有 anti-sg 那种牛魔的东西。我们只需要判断 \(n-1\) 的时候是否必胜即可。

然后这个游戏如果没有 \(k\) 的限制,就很像一个经典的 fib 博弈形式:

  • \(n\) 个石子,两个人轮流取。第一个人第一步不能全取,然后每个人每次取的不能超过上一个人的两倍,问谁有必胜策略。

结论是:如果 \(n\) 等于某个 fib 数 \(f_k\) 则先手必败,否则先手必胜。

前一部分的结论需要归纳证明:

  • \(n=f_{k}\) 的时候后手必胜且后手总能在最后一步取的石子个数 \(\le \frac{2}{3}n\)

实际上不难归纳,这个结论也比较优美。利用这个结论就可以证当 \(n\) 不为 fib 数的时候的必胜性,方法如下:

\(n\) 拆成泽肯多夫表示,然后找到拆出来的最小的 \(f\),先手直接取走 \(f\) 个石子即可,此时设次小的是 \(f'\),由于后手无法一次拿走 \(\ge f'\) 个,此时直接套用 \(n=f_k\) 情况的结论即可。

那么现在回到原题:发现如果 \(k\ge n\) 的时候先手必胜(因为我们没有第一次不能取完的限制);否则如果 \(k\ge f\) 也还是必胜的。而如果 \(k\lt f\),此时再套用 \(n=f_k\) 情况的结论,先手肯定是必败的。

那么我们就研究出了一个充要条件:当且仅当 \(k\ge f\) 的时候必胜,其中 \(f\)\(n\) 在泽肯多夫表示下拆出来的最小的数。

而正整数的泽肯多夫表示其实就是一个 fib 数列的二进制表示,确定方式是从高位开始贪心的,完全可以类比二进制数。所以考虑用类似数位 dp 的的方式去做,后面的内容就都很平凡了。

时间复杂度 \(O(T\log n)\)

代码

D. 字符串

对比 SNOI2019 的签到,显然难度增长的很快。

\(k:=n-k+1\),则这个问题可以视作 \(k\) 个黑点和 \(k\) 个白点之间的最大匹配,边权为 LCP。

\(2k\) 个串全部插入字典树:则我们相当于在树上寻找距离和最小的匹配。经典的树上贪心告诉我们我们自底至顶合并是对的。

当然我们没法真的建出来这棵树(其实也能直接建后缀树)。我们直接从大到小枚举深度:然后暴力匹配能在这个深度匹配的节点即可,这就是把树上贪心匹配的过程隐式化了而已。

这相当于在 SA 的 height 数组上从大到小激活相邻的连边,然后激活前贪心匹配左右两遍的黑白点。其实到这里做法就比较随意了,随便换个 SAM 什么的也能做。时间复杂度 \(O(n\log n)\)

代码

F. 水池

质量很高的 DS 题。如果这个题像 NOI2023 那样给足大样例那就更好了,但据我所知 SNOI 场上没给大样例啊?

首先把这个抽象的东西先整理成代码语言,考虑老老实实写个 \(n=500\) 之类的东西来过样例,后两个操作显然比较平凡,先考虑注水:

  • 如果 \(a_x \ge y\),那么就不会发生任何事情。
  • 否则我们发现除了 \(a_x\) 会变成 \(y\) 以外,两侧的一些位置的水面也会变成 \(y\)。具体而言如果 \(h_{x-1}\ge y\)\(x-1\) 以及往前的位置就不会变化,否则 \(h_{x-1}\) 也会提升到 \(y\)(一定有 \(h_{x-1}\lt y\),否则 \(a_x\) 就会 \(\ge y\)),然后继续向前考虑 \(h_{x-2}...\)。对于 \(x+1\) 这部分来说也是同理的。

注水看上去还是比较平凡的。注意部分分缺少的是抽水,所以这个应该就会棘手一些:

  • 显然除了 \(a_x\) 会下降到 \(0\),两侧的一些水面也有一定程度的下降。设原始水面为 \(y\),那么这个影响到的范围应该和注水操作一样:都是找到两侧第一个 \(\ge y\) 的挡板。然后对于一个 \(z\lt x\) 的位置,它的水面应该下降到 \(\min h_{z\sim x-1}\),对于一个 \(z\gt x\) 的位置,它的水面应该下降到 \(\min h_{x\sim z-1}\)

这样,我们就把原问题整理的清晰了很多。然后继续来从部分分考虑:

首先,我们可以建出关于操作的树形结构,然后在上面 dfs,这样我们就只需要支持撤销即可,那么写起来会容易一些。当然直接硬上动态开点的可持久化空间是完全一致的。

其次,如果没有二操作,发现可以线段树二分出左右边界,然后变成了一个区间 cover,是容易处理的。不妨令 \(f_i = h_i,g_i = h_{i-1}\),那么我们向左求出最大的 \(l\) 使得 \(f_l\ge y\),向右求出最小的 \(y\) 使得 \(g_r \ge y\),那么 \((l,r)\) 区间就是一/二操作影响到的范围。

对于二操作,涉及到这种,前缀最大值,后缀最大值相关的内容,都考虑使用单侧递归线段树(或者类似思想)来解决。本题只需要借用它的一些思想即可:我们考虑现在要做这样一个事情:

给出 \((l,r,x)\),对于线段树节点 \([l,r]\),把每个 \(a_i\) 替换成 \(\max(x,g_{l\sim j})\)

由于我们是单点求值,直接打 tag 就好了,唯一需要处理的就是 tag 的下放:注意到,放给左端点的时候,应该是 \((l,mid,x)\),放给右端点的时候,应该是 \((mid+1,r,\max(x,g_{l\sim mid}))\),因此我们额外维护出区间 \(g\) 的最大值即可。

对于 \(f\) 也有一个类似的操作,只不过改成了后缀取 \(\max\),二者是对称的,而且覆盖操作的标记是后面的覆盖前面的,所以可以同时维护两个操作的懒标记。这个时候我们就解决了二操作。时间复杂度 \(O(m\log n)\)

一个需要注意的点是:由于 \(f/g\) 是会被修改的,而叶子节点的答案实际上需要结合懒标记和 \(f/g\) 结算而来,所以如果我们先修改 \(f/g\) 再求这个位置的答案可能会出问题:解决方案是我们每次修改 \(f/g\) 前先把真实答案算出来存在叶子上,然后把懒标记清空,然后再修改 \(f/g\)

代码

CCPC 秦皇岛

一样,如果联赛没退役就抽空继续补。

C. Palindrome

以下假设我们的区间是 \([1,n]\)(因为如果是 \(l,r\) 的话会容易和删除区间 \([L,R]\) 混淆)。

首先题面也说了特判回文。然后我们考虑枚举一个回文中心 \(p\),这个回文中心是删除了 \([L,R]\) 的回文中心。首先显然这个回文中心不会被 \([L,R]\) 包含,换言之 \([L,R]\) 只会出现在枚举 \(p\) 后字符较多的那一边:所以考虑按照 \(mid\) 为分界点,\(mid\) 左边的 \(p\),它对应的 \([L,R]\) 一定在 \(p\) 右侧,而 \(mid\) 右边的 \(p\) 一定在 \(p\) 左侧。

现在不妨来考虑 \(mid\) 在左侧的情况:那考虑就是,\(p\sim L-1\) 这一侧要和 \(p\) 右侧对上,然后 \(R+1\sim n\) 这一侧和 \(1\) 开头的一段对上。所以我们现在预处理一个东西:就是把 \(L\)\(R\) 匹配,然后匹配 \(L+1,R-1\),一直下去,能匹配的最长长度 \(i\)。那么我们一定要满足 \(R\ge n-i\),然后还有就是以 \(p\) 作为回文中心可以覆盖到 \(L-1\) 这个位置。

那其实枚举 \(p\) 后最短的 \([L,R]\) 长度就可解了:我们会要求 \(L\)\(p\) 的距离大于等于 \(i\)\(p\) 的距离,然后 \(R\) 直接取到 \(n-i\)。那我们发现其实就是在找一个最靠近 \(mid\)\(p\) 使得 \(mid\) 为中心的回文串能覆盖到 \(i\),差不多是这个东西(有些地方可能有 \(+1/-1\) 的 typo)。\(\gt mid\) 的同样做,两边取个 \(\min\) 就是答案。

然后我们还要对答案计数:我们确定 \(p\) 以后,刚才找到的是一个 \(L\) 最小的解,考虑右移 \(L\) 就要求它和关于 \(p\) 对称的那个字符是相同的,那么我们只要知道以 \(p\) 为中心的最长延伸长度,就能算出答案了。而我们其实早就知道这个了。

口胡起来容易但是写起来还是比较麻烦的:容易看出回文中心可能是一个字符也可能是两种字符之间,两种情况都要考虑,然后就会有很多地方会有 \(+1/-1\) 的要求,然后还要写一下 seg 二分来支持快速找到 \(mid\) 两侧最优秀的 \(p\)。时间复杂度 \(O(q\log n)\)

代码

E. Coloring Tape

这个题第一眼看就感觉是那种按列状压 \(dp\) 什么的。第一想法是我们直接枚举颜色的分界点,但发现一个问题就是我们需要知道刷子在哪里。那我们考虑直接记录刷子的位置,此时注意到一个性质:就是对于任何一个合法的涂色方案,我们从第一列开始涂,到最后一列的时候每个刷子的位置是确定的。所以我们设 \(dp(i,mask)\) 表示考虑完前 \(i\) 列的涂色,然后刷子落在 \(mask\) 这些位置的涂色方案数即可。

如何转移?首先由于有些颜色会在这一列停止,所以我们应该是选择 \(mask\) 的一个子集 \(mask'\),然后这些刷子水平向右。然后我们还要用 \(mask'\) 的刷子把整个列刷满,最后它们位于的位置 \(mask''\) 才是我们要贡献到的地方。

第一步是容易的:我们令辅助状态 \(g(mask)\) 是所有 \(mask\subseteq S\)\(dp(i,mask)\) 之和即可。关键是第二部分,我们会发现一个初始的刷子状态 \(mask\) 会有很多种方式涂满这一列。

发现其实也没有那么随意:如果有两个相邻的刷子我们可以从中间劈开,因为左边的刷子只会向左刷,右边的刷子只会向右刷。所以我们可以考虑相邻两个刷子都不相邻的状态。如果有 \(n\) 个刷子,那么要么有 \(n\) 个空要么有 \(n-1\) 个空,如果有 \(n+1\) 个空那就是无法涂色了。

  • 如果有 \(n\) 个空,比如说以最左边的刷子左侧还有空位为例,此时所有刷子只能向左刷,所以只有一种方式。
  • 如果有 \(n-1\) 个空, 那么应该是存在一个空,然后以其中的一个位置为分界点,左侧的全部向右刷,右侧的全部向左刷,此时就会有 \(O(n)\) 种涂色方式。

然后一个状态的涂色方式应该是我们断开以后全部相乘。但你注意到 \(n=14\),所以好像爆搜一下所有情况,会发现所有状态的涂色方式之和加起来也没有太多。所以我们其实爆搜所有转移即可。

如果实现的不好,依然有概率会 TLE:我们不需要对每一列都重新搜一次转移,因为涂色方式只和 \(mask\) 有关。初始的时候存储一下每个 \(mask\) 能到的所有转移,和对应的颜色数组。然后每一列的转移过程中直接调用即可。这样其实把复杂度砍掉了一个 \(n\),会快非常多。

代码

L. Yet Another Maximize Permutation Subarrays

先考虑给一个排列怎么求答案,其实我们令 \(p_i\)\(i\) 这个值的位置,对每个 \(x\),求出 \(\min p_{1\sim x}\)\(\max p_{1\sim x}\) 即可判断是否存在大小为 \(x\) 的子排列。我们设 \(f(x)\) 表示是否 \(1\sim x\) 连通。

再来考虑:我们做一次交换操作,则如果 \(f(x)\)\(0\) 变成 \(1\),直观来看 \(1\sim x\) 这些数原本就应该是近乎连通的,不可能说一次操作让很多段就合并到一起了。我们发现我们做交换操作的时候,还会让一些 \(f(x)\)\(1\) 变成 \(0\)。不妨先来研究这个,因为 \(f(x)=1\) 的意味着原本 \([1,x]\) 都在一段。

不妨假设 \(i\le j\),则什么样的 \((i,j)\) 会让 \(f(x)\)\(1\) 变成 \(0\)?不妨假设 \(1\sim x\)\([L,R]\) 出现。

  • \(i\lt L-1\) 的时候,显然当且仅当 \(j\in [L,R]\) 的时候会出问题,但是需要特判 \(x=1\),注意到 \(x=1\) 的话 \(f(x)\) 始终为 \(1\) 那就不用管了,以后都默认 \(x\gt 1\)

  • \(i=L-1\) 的时候,交换 \((i,R)\) 是正确的,所以非法的 \(j\) 位于 \([L,R)\) 中。

  • \(i=L\) 的时候,交换 \((i,R+1)\) 是正确的,所以非法的 \(j\) 位于 \((R+1,n]\) 中。

  • \(i\in (L,R]\) 的时候,显然非法的 \(j\) 位于 \((R,n]\) 中。

  • \(i\gt R\) 的时候,没有非法的 \(j\)

考虑按顺序枚举 \(i\),然后令 \(g(j)\) 表示此时 \((i,j)\)\(f=1\) 的那些段的影响数量,则可以用树状数组维护 \(g\) 的变化,这部分是 \(O(n\log n)\) 的。

再来考虑什么时候会让 \(f(x)\)\(0\) 变成 \(1\),很自然地,如果 \(1\sim x\) 的出现恰好被划分成了两段,那么是有可能通过一次交换使得 \(1\sim x\) 在一起的:我们设两段是 \([a,b]\)\([c,d]\)\(b\lt c-1\))。

  • 如果 \(b=c-2\),那么我们发现 \((a,b+1)\)\((b+1,d)\) 都是合法的。
  • 事实上可能会觉得没有别的操作了,但是这个题最重要的点就是要考虑连续段长度为 \(1\):如果 \(a=b\),那么就算没有 \(b=c-2\)\((a,c-1)\)\((a,d+1)\) 也是合法的。同理如果 \(c=d\),那么 \((a-1,c)\)\((b+1,c)\) 始终是合法的。

好像就是全部了?再想想会发现如果分成了三段的时候,也是有可能的,还是因为可能有一些段长度为 \(1\):假设出现分成三段 \([a,b],[c,d]\) 以及 \([e,f]\) 的话,如果 \(a=b\)\(d=e-2\),那么 \((a,d+1)\) 是可行的;同理如果 \(e=f\)\(b=c-2\) 那么 \((b+1,e)\) 也是可行的。看上去没有其它的可能了,段数再多也显然不可能了。

总而言之就是,确定了 \(x\) 以后只有 \(O(1)\) 对可能会让 \(f(x)\)\(0\) 变成 \(1\),那么我们枚举这些对即可:而每一对 \((i,j)\) 会让多少 \(f(x)\)\(1\) 变成 \(0\) 可以离线 \(O(\log n)\) 计算,时间复杂度就做到了 \(O(n\log n)\)

一个遗留问题是如何找到我们上述说的那些 \([a,b],[c,d],[e,f]\) 之类的,按照 \(x\) 从小到达枚举后,用序列上的 dsu 以及 bit 上倍增的手法,随便搞搞就行了反正。

跑的比我想象的要快。

代码

M. Inverted

感觉这个题还是比较趣味的。

容易想到对 \(n-1\) 个时刻分别 dp,某个时刻的树可以这样描述:

有些点 \(u\) 有一个对应的拓展点 \(u+n\)。 然后对于一条边 \((u,v)\)

如果 \(u+n\)\(v+n\) 都存在,那么就连接他们两。否则如果 \(u+n\) 存在,就连接 \((u+n,v)\),否则如果 \(v+n\) 存在,就连接 \((u,v+n)\)

发现其实连边还是遵循着树形关系的,两棵树之间的两个点如果有连边,那么他们的位置关系放在两颗树上都是父子关系。

所以想到令 \(dp(u,0/1)\) 表示我们考虑完了 \(u\) 的两棵子树内的边集,然后 \(u\)\(u+n\) 是否连通,这样一个方案数。如果最后 \(1+n\) 不存在那答案就是 \(dp(1,0)\) 否则答案就是 \(dp(1,1)\)

合并的时候直接枚举 \(u,v\)\(u+n,v+n\)\(u+n,v\),还有 \(u,v+n\) 四条边的存在性,既要保证不能出现环,还要保证不能出现一个部分和上面的不再连通,可以每次用 \(4\) 个点的并查集来判连通性。

这样做一次 dp 就是 \(O(n)\) 的,但显然这个 \(O(n^2)\) 的做法就过不去,常数太大了。

我们不需要每次都重算,类似 ddp 那种东西,我们每次激活一个点 \(u\) 只需要重算 \(u\rightarrow 1\) 路径上所有 dp 的答案即可。这样突然就跑的飞快了。

代码

EC-Final 2022

A. Coloring

好题。

首先注意到 \(a_i\rightarrow i\) 的连边构成外向树森林,而我们只关注 \(s\) 所在的外向树森林,也就是只需要考虑一颗基环树的情况。

不妨考虑 \(s\) 在环外的情况,此时我们只关注以 \(s\) 为根外向树。注意到一个性质:任何局面下,不会有两个 \(1\) 的中间出现 \(0\)。所以可以设 \(dp(u,0/1)\) 表示考虑 \(u\) 子树内的答案,且根节点是 \(0/1\) 的情况。转移的时候,我们可以讨论三种情况:\(v\) 不选;\(v\) 选,且 \(c_v=1\)\(v\) 选且 \(c_v=0\)。如果 \(dp(u,0)\) 转移到了 \(dp(v,0)\),那么代价就是 \(2p_v\)(第一次把 \(1\) 传递过来,然后 \(v\) 这里传递下去以后,\(u\) 再把 \(0\) 传递过来)。 此时可以 \(O(n)\) 解决。

现在考虑基环树上的情况。我们以 \(s\) 为开头拉出这个环: \(r_1,r_2,...,r_m\)

注意到,我们可以做这样一个事情:把 \(s=r_1\) 所在的 \(1\) 放给 \(r_2\),然后 \(r_m\)\(0\) 覆盖 \(r_1\)。这样就把 \(1\) 向右推了一次。

\(m\) 次以后,环上的每个点就都满足:存在一个时刻是 \(1\),过一段时间后又变成 \(0\)。称这个事情为基本操作。

由于我们能做任意多次这样的操作,所以环上的一个点的颜色可以是:\(101010\) 这样交替的。

那这就和树的情况不一样了:考虑环上一个点 \(u\) 所挂着的子树,如果 \(u\) 被访问了 \(k\) 次(第一次一定是 \(1\),第二次一定是 \(0\),以此类推),那么它的子树内,就允许出现 \(k\) 次颜色交替(最后一次一定是 \(1\))。在 \(s\) 位于环外的情况下这个 \(k\) 一定 \(\le 2\)。因为第一次是 \(c_u=1\) 的访问,第二次是 \(c_{a_u}\rightarrow c_u\) 的访问,然后我们再也不能够让 \(c_u\) 重新 \(=1\) 了,所以树上的情况很简单。而此时我们就需要重新更改一下 \(dp\):设 \(dp(u,k,0/1)\)\(u\) 子树内允许 \(k\) 次颜色交替,然后 \(u\) 的颜色是 \(0/1\) 的一个最大收益。转移的时候枚举 \(v\) 这里的允许交替次数 \(x\)。然后代价这里加上 \(p_v\times (x+1)\) 即可,这个 dp 很容易做到 \(O(n^2)\)。然后我们就考虑完了环外子树的情况,可以专心来考虑环。

首先很显然的是这个环上为 \(1\) 的点还是构成一段区间:有两种情况。

  • 这个区间在我们的链上可以表示成 \(r[x...y]\) 的形式。
  • 这个区间被表示成了 \(r[1...x]\)\(r[y..n]\) 并起来的形式(\(x\le y\))。

对于第一种情况,那就是 \(r_1\) 开始推到 \(r_y\)(这部分 \(c=1\)) 和 \(r_m\) 推给 \(r_1\) 后再推到 \(r_{x-1}\)(这部分 \(c=0\))两部分组成。

对于第二种情况,那就是 \(r_1\) 开始推到 \(r_y\),然后 \(r_m\) 推给 \(r_1\) 再推到 \(r_{y-1}\),然后再从 \(r_y\) 一直推到(越过 \(r_m\)\(r_x\)

我们先枚举基本操作的执行轮数,然后上面的两种情况都很容易 \(O(n)\) 计算。这样就在 \(O(n^2)\) 的时间内解决了。

代码

G. Rectangle

首先考虑三条 \(x\) 和两条 \(x\) 的情况,剩下的可以交换 \((x,y)\) 以后一样做。

对于三条 \(x\),其实就是一个一维问题:我们只关注 \(x\) 坐标构成的区间。

因此可以做一个基本的离散化:加入 \(1\),以及每个 \(l_i,r_{i}+1\)。这样的话,会把 \([1,M]\) 划分成 \(O(n)\) 个左闭右开的区间。那么由于每个区间内的数完全等价,可以直接缩成一个 \(O(n)\) 级别的序列,每个位置有一个点权 \(v_i\) 表示数量。

对于三个 \(x\) 的:可以令 \(dp(i,j)\) 表示考虑完了 \(\le i\) 的位置,最后有点的位置位于 \(i\),且选了 \(j\) 个的方案数。

考虑 \(dp(i,j)\rightarrow dp(p,k)\) 的转移(\(j\lt k\)):那就是说系数为 \(\dbinom{v_p}{k-u}\),且不能有区间位于 \((i,p)\) 这一段内。那么我们固定 \(p\) 的话,\(i\) 的取值有一个下界,这是容易算出的。因此用前缀和就可以 \(O(n)\) 计算。

重头戏是两个 \(x\) 的情况:不难想到,我们对 \(y\) 坐标做相同的离散化手法,然后枚举 \(y\) 点所在的区间。那么相当于考虑 \(y\) 坐标不包含这个点的所有矩形,他们的 \(x\) 那一维,用两条线段覆盖的方案数。

那么在 \(y\) 坐标轴上线段树分治一下对吧,现在就变成做一个需要支持回撤的问题:

  • 动态加入区间,问选两个点出来覆盖所有区间的方案数。

这个东西的话,考虑讨论一下:

  • 选出来的两个位置在一个点里。那么我们只需要实时维护所有区间的交。然后交里的 \(\sum \dbinom{w_i}{2}\) 就是答案。非常容易。
  • 不在一个点里,那么直接对每个点维护:他为左侧点的时候,右侧点的取值范围。记这个区间为 \(f_i\)。那么它由 \(L,R\) 组成。

加入 \([l,r]\) 的时候,首先 \(\gt r\) 的位置就被 ban 掉了。然后对于 \(\lt l\) 的这个前缀就要把对应范围和 \([l,r]\) 交一下。

那么对 \(L,R\) 分开维护,注意到双单调性的存在。所以前缀 chkmin/chkmax 就变成区间 cover 了。直接线段树二分一下。

然后回撤也是很容易的。做完了。超级恐怖 \(2\log\)

代码

EC-Final 2022

A. Coloring

好题。

首先注意到 \(a_i\rightarrow i\) 的连边构成外向树森林,而我们只关注 \(s\) 所在的外向树森林,也就是只需要考虑一颗基环树的情况。

不妨考虑 \(s\) 在环外的情况,此时我们只关注以 \(s\) 为根外向树。注意到一个性质:任何局面下,不会有两个 \(1\) 的中间出现 \(0\)。所以可以设 \(dp(u,0/1)\) 表示考虑 \(u\) 子树内的答案,且根节点是 \(0/1\) 的情况。转移的时候,我们可以讨论三种情况:\(v\) 不选;\(v\) 选,且 \(c_v=1\)\(v\) 选且 \(c_v=0\)。如果 \(dp(u,0)\) 转移到了 \(dp(v,0)\),那么代价就是 \(2p_v\)(第一次把 \(1\) 传递过来,然后 \(v\) 这里传递下去以后,\(u\) 再把 \(0\) 传递过来)。 此时可以 \(O(n)\) 解决。

现在考虑基环树上的情况。我们以 \(s\) 为开头拉出这个环: \(r_1,r_2,...,r_m\)

注意到,我们可以做这样一个事情:把 \(s=r_1\) 所在的 \(1\) 放给 \(r_2\),然后 \(r_m\)\(0\) 覆盖 \(r_1\)。这样就把 \(1\) 向右推了一次。

\(m\) 次以后,环上的每个点就都满足:存在一个时刻是 \(1\),过一段时间后又变成 \(0\)。称这个事情为基本操作。

由于我们能做任意多次这样的操作,所以环上的一个点的颜色可以是:\(101010\) 这样交替的。

那这就和树的情况不一样了:考虑环上一个点 \(u\) 所挂着的子树,如果 \(u\) 被访问了 \(k\) 次(第一次一定是 \(1\),第二次一定是 \(0\),以此类推),那么它的子树内,就允许出现 \(k\) 次颜色交替(最后一次一定是 \(1\))。在 \(s\) 位于环外的情况下这个 \(k\) 一定 \(\le 2\)。因为第一次是 \(c_u=1\) 的访问,第二次是 \(c_{a_u}\rightarrow c_u\) 的访问,然后我们再也不能够让 \(c_u\) 重新 \(=1\) 了,所以树上的情况很简单。而此时我们就需要重新更改一下 \(dp\):设 \(dp(u,k,0/1)\)\(u\) 子树内允许 \(k\) 次颜色交替,然后 \(u\) 的颜色是 \(0/1\) 的一个最大收益。转移的时候枚举 \(v\) 这里的允许交替次数 \(x\)。然后代价这里加上 \(p_v\times (x+1)\) 即可,这个 dp 很容易做到 \(O(n^2)\)。然后我们就考虑完了环外子树的情况,可以专心来考虑环。

首先很显然的是这个环上为 \(1\) 的点还是构成一段区间:有两种情况。

  • 这个区间在我们的链上可以表示成 \(r[x...y]\) 的形式。
  • 这个区间被表示成了 \(r[1...x]\)\(r[y..n]\) 并起来的形式(\(x\le y\))。

对于第一种情况,那就是 \(r_1\) 开始推到 \(r_y\)(这部分 \(c=1\)) 和 \(r_m\) 推给 \(r_1\) 后再推到 \(r_{x-1}\)(这部分 \(c=0\))两部分组成。

对于第二种情况,那就是 \(r_1\) 开始推到 \(r_y\),然后 \(r_m\) 推给 \(r_1\) 再推到 \(r_{y-1}\),然后再从 \(r_y\) 一直推到(越过 \(r_m\)\(r_x\)

我们先枚举基本操作的执行轮数,然后上面的两种情况都很容易 \(O(n)\) 计算。这样就在 \(O(n^2)\) 的时间内解决了。

代码

G. Rectangle

首先考虑三条 \(x\) 和两条 \(x\) 的情况,剩下的可以交换 \((x,y)\) 以后一样做。

对于三条 \(x\),其实就是一个一维问题:我们只关注 \(x\) 坐标构成的区间。

因此可以做一个基本的离散化:加入 \(1\),以及每个 \(l_i,r_{i}+1\)。这样的话,会把 \([1,M]\) 划分成 \(O(n)\) 个左闭右开的区间。那么由于每个区间内的数完全等价,可以直接缩成一个 \(O(n)\) 级别的序列,每个位置有一个点权 \(v_i\) 表示数量。

对于三个 \(x\) 的:可以令 \(dp(i,j)\) 表示考虑完了 \(\le i\) 的位置,最后有点的位置位于 \(i\),且选了 \(j\) 个的方案数。

考虑 \(dp(i,j)\rightarrow dp(p,k)\) 的转移(\(j\lt k\)):那就是说系数为 \(\dbinom{v_p}{k-u}\),且不能有区间位于 \((i,p)\) 这一段内。那么我们固定 \(p\) 的话,\(i\) 的取值有一个下界,这是容易算出的。因此用前缀和就可以 \(O(n)\) 计算。

重头戏是两个 \(x\) 的情况:不难想到,我们对 \(y\) 坐标做相同的离散化手法,然后枚举 \(y\) 点所在的区间。那么相当于考虑 \(y\) 坐标不包含这个点的所有矩形,他们的 \(x\) 那一维,用两条线段覆盖的方案数。

那么在 \(y\) 坐标轴上线段树分治一下对吧,现在就变成做一个需要支持回撤的问题:

  • 动态加入区间,问选两个点出来覆盖所有区间的方案数。

这个东西的话,考虑讨论一下:

  • 选出来的两个位置在一个点里。那么我们只需要实时维护所有区间的交。然后交里的 \(\sum \dbinom{w_i}{2}\) 就是答案。非常容易。
  • 不在一个点里,那么直接对每个点维护:他为左侧点的时候,右侧点的取值范围。记这个区间为 \(f_i\)。那么它由 \(L,R\) 组成。

加入 \([l,r]\) 的时候,首先 \(\gt r\) 的位置就被 ban 掉了。然后对于 \(\lt l\) 的这个前缀就要把对应范围和 \([l,r]\) 交一下。

那么对 \(L,R\) 分开维护,注意到双单调性的存在。所以前缀 chkmin/chkmax 就变成区间 cover 了。直接线段树二分一下。

然后回撤也是很容易的。做完了。超级恐怖 \(2\log\)

代码

CCPC 哈尔滨

C. Karshilov's Matching Problem II

很好的字符串题。

考虑,如果 \(T[i,j]\)\(S\) 的前缀,则 \(T[i,j-1]\) 一定也是。因此,令 \(f_i\)\(T[i...n]\)\(S\)\(LCP\) 长度,对于一次询问,所问的就是:\(\sum_{i=l}^{r}sum_{\min\{f_i,r-i+1\}}\)

\(f_i\) 可以哈希二分/Z函数求出,然后对于一个 \(i\) 来说,一定存在一个分界点 \(p\),使得 \(r\le p\) 的时候 \(\min\) 取到 \(r-i+1\)\(r\gt p\) 的时候 \(\min\) 取到 \(f_i\)。而 \(p\) 容易求出。

对于第二种情况带来的贡献,可以离线以后,按照 \(r\) 扫描线后加上树状数组 \(O(q\log)\) 带走。

关键是第一种贡献,也即 \(sum_{r-i+1}\) 这个东西,难以在扫描线的时候维护。

从原串的视角来看会有很好的发现:考虑所有 \(f_i\ge r-i+1\) 的位置 \(i\),注意到,其构成 border 关系!

所以我们只需要求最小的 \(p\) 使得 \(T[p...r]\)\(S\) 的前缀,然后就是 fail 树上的前缀和。而这就是 kmp 所做的事情,由于还要让 \(p\ge l\) 所以需要在 fail 树上倍增一下。

时间复杂度 \(O((n+q)\log)\)

代码

F. Palindrome Path

这个题的做法非常神妙!!

考虑既然是回文,就可以从起点终点开始同时往回构造:但是我们注意到,操作并不完全可逆,因为如果我们移动的方向是不可行位置我们会原地不动(而一般题目里这都是不允许的走法),因此很有可能就是会出现,一边不能走,另一边却能走的情况。本题也极有可能是利用这种操作来完成目的。

考虑规约一些基本操作,结论是,我们可以做到:在操作序列的开头结尾添加若干对称的操作,使得 \((ex,ey)\) 不变,但是 \((sx,sy)\) 移动一步(前提是该位置可以通行)。而利用这个基本操作,我们直接从 \((sx,sy)\) 开始 dfs 一遍所有格子,然后走到 \((ex,ey)\) 即可。

而具体这个基本操作怎么实现呢?不妨假设现在要把 \((sx,sy)\) 向右移动一格。我们让起点向左走(那么终点就应该不断向右),直到有一边无法通行为止,假设我们此时移动了 \(k\) 次。

  • 如果说,终点这边可以继续动,那就说明左边一定动不了了。我们此时在让起点向左移动一次(它会留在原地),然后终点会向右一格。然后我们再让起点向右移动 \(k+1\) 步。这样起点移动到 \((sx,sy+1)\),而终点因为多行进了一次,会留在原来的位置!
  • 否则,终点无法继续行动。直接让起点此时向右移动 \(k+1\) 步。此时好像看上去不对,因为终点也会动 \(k+1\) 步变成 \((ex,ey-1)\)。但是其实我们发现终点是 \((ex,ey-1)\)\((ex,ey)\) 都是一样的结果(因为后者向右会被拦住一次),所以此时也是合法的。

然后我们就实现了这个基本操作,解决了这个问题。

代码

ICPC 南京

D. Red Black Tree

膜拜一下场上一血的 gary 队。

首先考虑直接 dp:设 \(f(u,j)\) 是仅考虑 \(u\) 为根的子树,然后 \(u\) 到每个叶子的路径上都恰有 \(j\) 个黑点,所需要的最少操作次数。

那么注意到一个事情:如果 \(u\)\(\ge 2\) 颗子树,我们的 \(j\) 的上界(记作 \(lim_u\))就只用设到深度浅的那边。那么 \(lim_u\) 就从本来的 \(dep_u\) 变得更小,事实上根据长剖优化 dp 的理论,这样的 \(\sum lim_u\) 就应该是 \(O(n)\) 的。

但是我们还要处理 \(u\) 只有一个儿子的情况。

显然这个时候 \(ans(u)=ans(v)\),所以考虑尝试缩掉这些 \(u\),然后每条边都带权重:\((x,y)\) 表示上面挂了 \(x\) 个黑色点,\(y\) 个红色点。

转移的时候,由于新增的黑点个数可以在 \([0,x+y]\) 这个区间里任选,代价是 \(|cnt-x|\)。那么讨论一下 \(cnt\)\(x\) 的大小关系,大概就是维护一下 \(dp(i)-i,dp(i)+i\) 两个东西的区间最值,st 表辅助一下即可。然后时间复杂度 \(O(n\log n)\)

代码

E. Extending Distance

很神的题!

首先考虑,让最短路增加 \(k\) 是很陌生的东西,也很难处理。但这是一个网格图左侧到右侧的形式,和平面图最小割转对偶图最短路的那个结果很像。最重要的一点是给定一张网络流图,问最少几次操作使得最大流恰好增加 \(k\),这是很容易想到用费用流解决的问题。

把题目给定的网格图,看成是一张 \((n+1)\times (m+1)\) 的网格图求最小割。更具体而言是这样转化的:

  • 首先是原图,我们新建虚拟源汇 \(s,t\),然后 \(s\) 向第一列的所有点连为 \(0\) 的边,第 \(m\) 列的所有点向 \(t\) 连为 \(0\) 的边。但这样还不符合对偶图的要求,因为 \(s,t\) 两个代表最外层平面的点,应该和平面图所有和外界有接触的面都连边。换言之就是第一行和最后一行之间的边并不存在,那么我们设成 \(\infty\) 即可,反正不影响最短路。
  • 然后是转化回去的网格图:考虑这个图的源汇应该就是图里的点,我们随便选第一列和最后一列的一对点都是合法的,不妨选取 \((1,1)\) 作为源,\((n+1,m+1)\) 作为汇。然后这张图左右两侧的边都应该不存在(因为为 \(0\)),上下两侧的边都是 \(\infty\),内侧的边恰好对应了输入的内容。

此时再跑费用流即可。

代码

XXII Open Cup,XiAn

我感觉除了一个签到,每个题都值得我写题解啊?

B. Might and Magic

四个属性自由分配,看着非常恐怖,因此它大概率存在很强的性质。

首先我们考虑到自身的防御属性:它只决定对方攻击我们的伤害。而其实我们不太关心对面攻击我们的具体伤害 \(d'\),而是关心 \(\lceil\frac{H}{d'} \rceil\) 罢了,所以根据数论分块,可能的防御属性只有 \(O(\sqrt H)\) 种(事实上,由于还有 \(C_p\) 的系数在,我猜测这个 \(O(\sqrt H)\) 在大部分情况下都是很常数很小的。)那么我们就已经解决一个了。

当我们确定了自身的防御属性 \(d\),也就确定了我们能进行的最多攻击次数 \(f\)(第 \(f\) 次攻击完就死了)。所以我们考虑来做这样一个问题:

要求在和不超过 \(n-d\) 的情况下最大化 \(f\) 次操作的攻击伤害。以下令 \(N:=n-d\)

首先,假设我们已经确定了 \(k\)(其实 \(k\) 就是法攻的操作次数):那么显然 \(k\) 的取值范围在 \([0,\min\{N,f\}]\) 之间,然后还剩下 \(N-k\) 点属性分配给法攻参数(执行 \(k\) 次)和普攻次数(执行 \(f-k\) 次):

  • 对于法攻来说,它的初始贡献为 \(0\):然后每分配一点属性点,就增加 \(C_m\times k\) 的伤害。
  • 对于普通来说,它的初始贡献为 \(C_p\times (f-k)\):然后我们如果要分配属性点,就要先投入 \(D_1+1\) 点属性点(可以认为是“激活”这个选择的代价),然后每分配一点属性点,就增加 \(C_p\times (f-k)\) 的伤害。

如果说没有那个 \(D_1+1\) 的激活代价,我们肯定是,看 \(C_m\times k\)\(C_p\times (f-k)\) 哪个大,然后只给那一边倾斜。事实上有激活代价也是同理的:如果我们激活了,那就说明 \(C_p\times (f-k)\) 这个增长量肯定必 \(C_m\times k\) 优秀,那么我们就会把法攻的属性点放给普攻,获得更大的增长量。

这就是我们所说的,很强的性质了!因为他直接让分配三个量的问题变成了两个量的问题。

  • 如果我们只给普攻倾斜,很显然就会让 \(k=0\),这是很容易 \(O(1)\) 计算的。
  • 如果我们只给法攻倾斜。那么显然造成的总伤害就是:\(C_m\times k\times (N-k) + C_p\times (f-k)\)。这是一个关于 \(k\) 的二次函数,其极值也非常容易 \(O(1)\) 求出。

然后本题就在 \(O(T\sqrt H)\) 的时间内解决了。

代码

C. 0 Tree

这一类构造题,有一类比较常用的做法:首先寻找齐全有解的必要条件,结合这些限制带来的提示,做出构造性的证明,使得它们也是充分条件。

显然 \(\oplus a\) 必须是 \(0\),整个过程中它都不会变化,并且显然 \(\sum b\le 0\),因为边权都是在不断增长的。

当然这些条件并不够强:我们考虑 \(n=2\) 的情况,若 \(a_1=a_2=15\)\(b=-1\),那么肯定也是无解的,但是没有被上面的条件判到。

我们不妨更认真地研究一下 \(n=2\) 的情况,它事实上能带来很多东西:首先是我们看到如果 \(-b\lt a\) 那么一定无解,然而就算 \(-b\ge a\) 也并不一定意味着有解。

\(-b\)\(a\) 的差值为偶数的时候,例如 \(a=9\)\(b=-15\) 的时候,我们可以先做两次 \(w=3\) 的操作,这样 \(a\) 不变而 \(b\) 变为 \(-9\)。然后做一次 \(w=9\) 的操作即可。所以若 \(-b\ge a\),那么 \(a+b\equiv 0\pmod 2\) 的时候一定有解;另一方面,如果 \(a+b\equiv 1\pmod 2\) ,可以说明一定无解:因为注意到,一次操作 \(x,y\) 的话,对于 \(x,y\) 以外的点,\(a+\sum b\) 的奇偶性不变(两条边的变化量都是 \(w\)),对于 \(x,y\) 两个点来说 \(a+\sum b\) 的奇偶性还是不变(因为一条边变化 \(w\),而点权也变化 \(w\))。所以我们又得到了一个比上面两条强不少的必要条件:\(\forall u,a_u+\sum_{e(u,v)\in E}b_e\equiv 0\pmod 2\)

这一点启发我们直接关注每个点相连边的权和:重定义 \(b_u\) 是和 \(u\) 相连的所有边的边权和。那么所要求的等价于 \(\forall u,a_u=b_u=0\)。而一次操作 \((x,y)\) 显然也只影响 \(a_x,a_y,b_x,b_y\)\(a_x,a_y\) 两个人就是简单的异或上 \(w\),而 \(b_x\) 肯定是增加 \(w\),至于 \(b_y\),需要根据路径长度的奇偶性来定:如果是奇数,那么 \(b_y\) 就是减去 \(w\);否则是增加 \(w\)

这启发我们把树黑白染色,然后注意到:同色点对的操作,不改变该颜色内的 \(\sum a,\sum b\);异色点对的操作,改变该颜色内的 \(\sum a\)\(\sum b\)(异或 \(w\)\(+w\))。注意到两种颜色的 \(\sum a\)\(\sum b\) 都是相同的。

为了让两种颜色内 \(\sum b\) 全变为 \(0\),我们只能利用异色点对间的操作:令 \(A=\sum a\)\(B=\sum b\)(某一种颜色的),则一个必要条件是 \(A\le -B\),同样是异或的值不超过加法的性质得到的,然后我们类似 \(n=2\) 地手法,三次操作就可以做到让 \(A=B=0\)

然后我们再利用同色点对的操作,对两种颜色的点独立考虑,使得所有的 \(a,b\) 都变为 \(0\)。以下皆只考虑黑色的点(白色同理)。

先让 \(a\) 全变为 \(0\),由于 \(A=0\) 所以我们直接选两个 \(a\neq 0\) 的点 \(u,v\),然后做 \((u,v,a_u)\) 就行了。

然后在此基础上再让 \(b\) 全变为 \(0\) ,一个很重要的没有被利用过的性质是 \(a\equiv b\pmod 2\),所以考虑:我们选两个点 \((u,v)\) 然后做两次 \((u,v,w)\),这样 \(b_u:=b_u+2w,b_v:=b_v-2w\),且 \(a\) 没有变化:利用这个操作,每次选一个正的 \(b\) 和负的 \(b\) 拿出来总能消掉一个,不断做这个过程就好了。

容易发现,其实操作次数是 \(O(3n)\) 级别的,完全可以通过 \(4n\) 的限制。

代码

D. Decomposition

很有启发性的一个构造题:

本题的构造方法是,我们把完全图拆分成 \(\frac{n-1}{2}\) 个长度大小为 \(n\) 的环,拼在一起得到一个完全图的欧拉回路。这条路上,任何两个同样的点,出现间隔都会大于 \(n-3\),然后直接在这条回路上按顺序拆分即可。

而具体的拆分成环的方式是:考虑把点 \(n\) 放在中心,然后 \(1\sim n-1\) 顺序在外圈围成一个环的结构。然后对于第 \(i\) 个环,我们把环上的 \(n-1\) 个点两两配对:第一组是 \(i\)\(i+1\),第二组是 \(i-1\)\(i+2\),第三组是 \(i-2\)\(i+3\),依次类推(在环上下标位于 \([1,n-1]\) 的意义下) 。。。 然后,\(n\) 作为出发点,第一步走到 \(i\)(也就是第一组的左端点),接下来第 \(j\) 组的右端点走向第 \(j+1\) 组的左端点,最后,第 \(\frac{n-1}{2}\) 组的右端点走回 \(n\)。把所有这样的环按序拼起来即可。

到这里这个题就在 \(O(n^2)\) 的时间内解决了,代码,它实质上也完成了下面问题的解答:

  • 给出一个 \(n\) 个点的完全图(\(n\equiv 1\pmod 2\)),将这个完全图拆分成 \(\frac{n-1}{2}\)\(n\) 元简单环。

其实还有几个类似的问题:

  • 给出一个 \(n\) 个点的完全图 \((n\equiv 0\pmod 2)\),将这个完全图拆分成 \(n-1\) 个大小为 \(\frac{n}{2}\) 的完美匹配。

这个问题的解答是类似的:依旧考虑点 \(n\) 放在中心,\(1\sim n-1\) 在外侧的圆环结构。然后第 \(i\) 组匹配,就让 \(n\)\(i\) 连边,然后 \(i-1\)\(i+1\) 连边,\(i-2\)\(i+2\) 连边,以此类推(下标依旧是在 \([1,n-1]\) 意义下)。

  • 给出一个 \(n\) 个点的完全图 \((n\equiv 0\pmod 2)\),将这个完全图拆分成 \(\frac{n}{2}\) 个哈密顿路径(注意不是环,而是路径)。

这个问题的解答依旧相似:考虑此时没有中心点,直接把 \(n\) 个点围成一个环。然后第 \(i\) 条路径的起点就是 \(i\),接着我们把 \(i-1\)\(i+1\) 匹配,\(i-2\)\(i+2\) 匹配。。。,最后会剩下 \(i+\frac{n}{2}\) 这个点。接着我们 \(i\rightarrow (i-1,i+1)\rightarrow (i-2,i+2)\rightarrow ...\) 即可,最后一步走到 \(i+\frac{n}{2}\) 就好了。这个做法也被称为 zig-zag pattern

E. Median

考虑,我们把 \(a_i\)\(i\) 全部拿出来,这些数是基准数,也就是一个集合的根本。然后 \(b_i-a_i\) 个余下的 \(i\) 才是我们需要考虑的对象。

发现一件事情:\(a_i\gt 0\)\(i\),和 \(a_i=0\)\(i\) 具有本质区别,称前者为红色,后者为黑色:则只要我们能把黑色数(可以同时为了平衡放入一些红色数)放进集合里,那么剩余的红色数总能塞入集合里。对于一个红色数 \(i\) ,随便考虑一个基准数是 \(i\) 的集合,总能放在它的左侧 or 右侧来保持平衡。所以我们考虑黑色数的放法就可以,可以选择红色数辅助它们,但不要求全部用上。

我们大体而言,应该是选择两个数 \(x,y(x\le y)\),然后它们可以同时放进一个基准数在 \([x,y]\) 范围内的集合,以保持平衡。因此 \(x,y\) 两者有一者是红色就一定能放,如果都是黑色,则要求区间 \((x,y)\) 内至少有一个基准数。而此时所有的集合都是大小奇数的,因此我们可以单独跳出一个数 \(x\) 放在一个基准数 \(\le x\) 的集合内,但是一个集合只有一次这样的机会。

发现:\(x,y\) 都是黑色的这一部分很有意思,我们按照基准数的出现给所有非基准数分段(比如如果 \(2,5\) 是基准数,就分成了 \([1,2),(2,5),(5,n]\) 三段),那么 \(x,y\) 如果能同时消去等价于两者所在段不同。

这是一个很经典的,每次选两个不同组的人消去,问最后能否消完的模型:如果不存在绝对众数,显然最后就能只余下 \(sum\bmod 2\) 个人,偶数的话显然直接有解,奇数的话,由于我们是可以自由决定剩下的是谁的,所以当不止有一段的时候,我们让数值最大的黑色数余下,其它都消去;然后这个最大的黑色数,总能放入一个基准数 \(\le\) 它的集合,然后有解。

如果存在一个绝对众数,注意到,我们还有红色数的辅助存在:我们不断地把红色数和绝对众数匹配,如果到最后它不是绝对众数了,那么根据上面的分析,它就还是会有解的;否则,最后一定会剩下若干个这种数,记作 \(x\) 的话,只需要看 \(\le x\) 的基准数数量就可以了。

这样,我们实质上就在 \(O(n)\) 的时间内解决了这个问题。虽然本题的 case 看似有些繁多,但根据上面这样的分析,其实并不难梳理明白。

代码

G. Power Station of Art

很精妙的题!

首先对每个连通块分别考虑,然后两个平凡的必要条件是,数值集合相同,且红色的奇偶性相同。

一个非常高明的视角是:我们考虑这个“如果颜色相同就同时反转”,不妨改成把(数值,颜色)捆绑在一起,然后每次操作的时候直接互换,然后在反转当前节点的颜色。好像这个手法就是那个经典撞蚂蚁出现的,但是每次见到都觉得非常深刻啊。

这样的话,考虑对于一个固定的 \((num,col)\),它移动了奇数步,颜色就会改变;反之偶数步就会不变。

这点启发我们进行二分图染色:然后直觉上,如果出现一个奇环,那就是一个很强的东西。

猜测说,对于一个连通块,只要有奇环,那么上面的两个必要条件就也是充分的。

证明肯定也是围绕奇环展开:我们先将每个节点的数字对好,然后说考虑利用我们的操作来调整颜色:对于任何两个有连边的点 \((x,y)\),都可以找到一条长度为偶数的路径,从 \(x\) 出发,经过 \(y\)(可能是非平凡路径):那么我们把 \(x\) 沿着这条路径换到 \(y\),然后 \(y\) 沿着路径换回去,最后直接再利用它们的直连边交换 \(x,y\) 一次。那么发现 \((x,col)\)\((y,col)\) 两个二元组位置没变,且颜色反转;路径上的所有二元组位置也没变,且恰好操作了两次,颜色不动。也就是我们在不改变其它任何东西的情况下,反转了有连边的两个点的颜色。然后我们随便找一颗生成树,从下往上利用这个操作把颜色对齐就好了,因为奇偶性是对上的,所以剩下根节点的时候就一定是合法的。

这个时候非二分图的情况就解决了,我们来考虑二分图下的情况。

肯定不是黑白的数值集合分别对应且红色奇偶对应这么简单。事实上应该是按照黑白的 \(0/1\) 异或上红蓝的 \(0/1\),分成的两个集合,数值集合分别对应相同就可以,而且不需要考虑红色的奇偶性。首先它肯定是必要的,因为移动的过程中黑白 \(xor\) 红蓝这个东西是不变的,其次它也是充分的,因为是连通的话,我们就能随便移动每个人到想去的位置,然后就能任意排列,所以充分性是很显然的。

这样我们就在 \(O(n\log n+m)\) 的时间内解决了这个问题。

代码

H. Command and Conquer: Red Alert 2

虽然过的人数是倒数第二少的,但其实并没有那么难,是个比较脑筋急转弯的题。

如果去想二分答案转判定反而会走偏!在无脑二分前需要先观察一些简单性质,这题在这一点上很有启发。(但感觉这样的题还是很少)

一个很必要的转化是,把一个人为中心的 \(2k\times 2k\times 2k\) 的正方体,改成一个人为左下角的 \(2k\times 2k\times 2k\) 的正方体,算出答案后除 \(2\) 上取整就是答案。

第一个观察是,我们会直接把三维的坐标分别调整成,所有关键点里,这一维的最小值。当我们意识到这一点的时候其实这个问题马上就解决了:因为我们考虑下一次不管往哪个方向走一步,都会有 \(x=minx,y=miny,z=minz\) 三者之一的点永远不再能被干掉,所以我们选择哪个方向,就必须干掉哪个方向上 \(=min\) 的所有点。我们贪心地选择令 \(2k\) 最小的一维干掉即可。时间复杂度 \(O(n\log n)\)

代码

一些散题

Luogu P2726 树的双中心

首先考虑从枚举两个点然后算答案:那么一定存在一条分界边:使得断开这条边以后,左边连通块交给第一个点管理,右边连通块交给第二个点管理。那么我们直接枚举这条断边,显然我们会在两个连通块里找重心。所以问题变成了删边后两个 part 的重心,也就是子树和子树补重心,这不是我们 CSP2019 吗?

当时那个题我们有绝世无敌炫酷数据结构对所有答案求和,但是这个题直接傻眼,因为他是要求出每种情况的具体答案的,这个时候我们需要一个树的重心的超级绝妙线性做法,不过这个东西需要排序一下来着,所以如果点带权就得有 \(\log\) 了。


我们下列的结论都基于树点带权的情况(边带权对重心的确定而言并不重要),因此重儿子是带权意义下的。令子树点权和为 \(sz\)。事实上树重心的很多结论都能通过比较基本的调整得到:

  • 给一个树加入一个带权点 \(u\) 的时候重心一定会朝这个方向移动若干步(可能不动)。且如果能从 \(u\) 移动到 \(v\),令 \(f_u\) 表示 \(u\) 这一块的点权和,则当且仅当 \(2f_u\gt f\) 的时候才能移动。

唯一需要用到的一个结论是:树的重心一定是重儿子所在子树重心的祖先。所以我们可以简单的求出每个子树的重心位置和对应答案:直接继承重儿子的重心,然后不断尝试把重心上移即可。分析复杂度可以发现,实质上是在每条重链上做双指针,因此是 \(O(n)\) 的。

接下来考虑子树补怎么求:我们发现一般情况下重心的移动没有什么好的性质,不妨考虑直接以重心做根:此时设 \(v\) 是重儿子,对于删去子树位于 \(v\) 子树以外的情况,重心一定会向 \(u\rightarrow v\rightarrow ...\) 这条重链偏移。我们直接按照删去子树的 \(f\) 排序:那么从小到大考虑,删去的 \(f\) 越大,重心就会越下沉,依旧双指针即可,这部分也是 \(O(n)\) 的。

否则我们删去的子树一定位于 \(v\) 内,此时重心一定会向次大子树 \(v'\) 的重链方向下沉。我们同样的手法就可以再次做到 \(O(n)\)。三部分除了排序外都是线性的,然后我们就解决了这个问题。

代码

UOJ 76 懒癌

绝世好题!

这个题面描述的过程太过于抽象了,所以我们先来研究一些很极端的部分分:比如两两之间互相可见。

如果只有一个人有红眼睛,他第一天就会出局。

如果只有两个人有红眼睛,对于一个红眼睛的来说,他第一天只看到一个红眼睛的,所以他不知道自己是什么颜色。但是第二天那个人没有出局,所以他知道,一定是对面那个人看到自己是红眼睛才没有出局,所以他们两个人都会同时出局。

如果只有三个人,类似地分析,我们可以发现第三天三个人全部出局。我们可以推出一个规律:就是如果有 \(k\) 个人是红眼睛,那么他们会在第 \(k\) 天一起出局。

通过上面的过程其实可以总结出一个人出局的方式:他会假设自己不是红眼睛,然后不断观测,直到第 \(t\) 天结束后:依旧没有人出局,但是根据他的假设(也就是自己不是红眼睛,且自己观测不到的人可以任意取),没有一种方式使得第 \(t\) 天没人出局,此时他会意识到自己的假设错误然后出局。

一个大体的感知就是:这个问题的关键在于信息的不对称,但是出局这个东西是全局可见的。而出局本身就传递了一些信息,对应的其实没有出局也对应了一些信息。所以我们其实是在不断用“没有人出局”来实现信息的传递。

并且可以感知到一点是:如果一个人从蓝眼睛变成红眼睛,整个过程的结束时间一定会延:因为没有人出局其实意味着场上的红眼睛会比一个人本来得到的信息能推测出来的要多,所以红眼睛越多,就会越晚推测完毕。

于是设 \(dp(S)\) 表示说:在 \(S\) 集合内的人恰好是红眼睛的情况下,会在哪一天最先出局。

显然 \(|S|=1\) 的时候 \(dp(S)=1\)。否则我们枚举 \(S\) 中的一个人 \(i\):现在 \(i\) 假设自己不是红眼睛,并且 \(i\) 看不见的人任取红蓝两种状态,在所有可能的 \(T\) 里找到最大的 \(dp(T)\),然后由于他的假设是错误的,所以 \(\max dp(T)\) 天过去后依旧不会有人出局,那么假设是错误的,他在 \(\max dp(T)+1\) 天出局;对所有的 \(i\) 求这个天数的最小值,就是答案。

但是我们发现其实是会出现环的:显然在环上,以及能走到这环的状态都是不会出解的,所以我们可以去掉这些点,假设是个 DAG 然后进行转移。那么至少我们从这个很绕的题面里提炼出一个指数做法。他的复杂度是 \(O(4^n)\) 级别的。

显然不会这么恐怖:因为红眼睛越多答案越大的性质,我们不需要枚举 \(T\):只需要假定 \(i\) 看不见的人全是红眼睛即可。这样复杂度降到了 \(O(2^n)\) 级别。

这样还是远远不够。我们建立一张图,如果第 \(i\) 个人无法看到第 \(j\) 个人,就连边 \(i\rightarrow j\)。则注意到我们选择 \(i\) 然后再确定 \(T\) 的过程,相当于是说,把 \(i\) 从红变成蓝,然后把他的后继全部染成红。而我们对所有 \(i\)\(\min\) 相当于是要在最少的步数内完成最终目标:所有点都是蓝色。

那么显然,如果某个环上一个点是红色,就不可能终止染色,因为我们会在这个环上不断的染。那么去掉所有环和能走到环的点后,变成了一个 DAG 上的问题。

DAG 上的最少步数是什么呢?我们肯定选择一个拓扑序最靠前的点然后 push 掉它,这样就不会出现,你 push 了以后,前面又 push 给你的情况。所以天数就应该是 DAG 上,所有红点能到达的点数。

那么第一次出局的人数呢?显然应该是初始那些,不会被别的红点到达的点数。

现在我们把这个问题整理成了一个非常清晰的图上问题:我们把两问要算的东西分别用算贡献的手法拆开:对于第一问,考虑对于一个点 \(u\),设有 \(cnt\) 个点能到达他(包含自身)。那么就有 \(2^{n}-2^{n-cnt}\) 种初始局面是能到达它的;对于第二问贡献则为 \(2^{n-cnt}\)。 那么只需要算出 \(cnt\) 即可,直接 bitset 传递闭包即可。时间复杂度 \(O(\frac{n^3}{w})\)

代码

AGC033D Complexity

以下假设 \(n,m\) 同阶,比较暴力的 \(O(n^5)\) 做法是很容易想到的:设 \(dp(x1,y1,x2,y2)\) 表示以 \((x1,y1)\) 为左上角和 \((x2,y2)\) 为右下角的矩形的答案即可。转移需要 \(O(n)\) 枚举断点。

注意到:我们总有 \(2\log n\) 的方案:每次任选行列的中点切开即可。所以考虑把答案维度压入 \(dp\),不妨设 \(f(t,x,y,z)\) 表示我们确定矩形的左边界是 \((x,y)\)\((z,y)\)(其中 \(x\le z\)),那么在 \(\le t\) 次操作下,合法的右边界最远能到什么位置;为了辅助转移,我们类似地设计一个 \(g(t,x,y,z)\) 表示确定矩形的上边界是 \((x,y)\)\((x,z)\)(其中 \(y\le z\)),那么在 \(\le t\) 次操作下,合法的下边界最远能到什么位置。

转移的时候,以 \(f\) 为例:如果当前这一步是竖直切开则很容易,调用 \(f(t-1)\) 的信息就能算出。否则我们不妨二分答案,然后问题变成了确定了上边界 \((x,y)\)\((x,ans)\),能否让下边界到达 \((z,y)\),此时调用 \(g(t-1)\) 的信息就能算出。

这样我们就在 \(O(n^3\log^2)\) 的时间内解决了这个问题。

代码

ARC092D Two Faced Edges

先来考虑如果原图是 scc 怎么做:注意到 \(n\le 1000\)\(m\le 10^5\) 的数据范围,结合 5s 的时限,猜测是 \(O(nm)\) 的做法。注意到朴素的暴力是 \(O(m^2)\) 的,考虑我们不需要对 \(m\) 条边都做答案:如果我们能保留更少的边,使得这个图依旧还是 scc,那么剩下的边反转与否都不影响答案。

显然对于一个点数为 \(x\)\(scc\) 可以保留不超过 \(2(x-1)\) 条边使得其依旧是强连通的,因为强连通等价于,对于一个点 \(u\) 而言,他能走到所有点,所有点也能走到他。对于第一点,我们直接 dfs 一遍,保留 dfs 过程中用到的 \(x-1\) 条边即可;对于第二点,我们在反图上 dfs 一遍就完成了。因此我们对于一个 scc 可以做到 \(O(nm)\) 的复杂度。

多个 scc 的时候就会有连接两个不同 scc 的边,显然此时等价于 DAG 的情况,则考虑 DAG 上的一条边反转怎么做:显然对于一条 \(u\rightarrow v\) 的边,如果反转后出现环,当且仅当 \(u\)\(v\) 之间还存在另外的 \(u\rightarrow v\) 的路径不经过这条边。那其实等价于 \(u\rightarrow v\) 的路径条数 \(\gt 1\)(我们并不需要利用到必经边这么强的性质)。所以直接做一个 \(O(nm)\) 的简单计算即可。

代码

CF436D Pudding Monsters

不是那个经典 DS(

考虑至少有一个怪兽不会动,不妨考虑先确定这些不动的位置 \(s_1,s_2,...,s_k\)。则 \(s_1\) 左侧的和 \(s_k\) 右侧的都会向 \(s_1/s_k\) 合并。对于 \(s_{i}\)\(s_{i+1}\) 之间的,一定是存在一个分界点,然后前半部靠到 \(s_i\) 后半部分靠到 \(s_{i+1}\)。这样我们就把这个动来动去的过程变得比较静态化了。

不妨直接设 \(f(i)\) 是假设第 \(i\) 个怪兽不动,然后 \(\le i\) 的怪兽匹配的最多个数。那么考虑转移的时候可以直接 \(O(m)\) 做事情:直接枚举和 \(i\) 右侧贴在一起的人匹配到的最大的 \(b_j\) 即可。然后我们解决了前一半,因为后一半人会向右和某个 \(a_j\) 合并。因此此时转移到的是辅助状态 \(g(i)\),表示说 \(i\) 去和左侧的人贴了,然后 \(\le i\) 的怪兽匹配的最多个数。

然后转移的时候,发现从 \(g(i)\) 枚举转移到谁比较困难。所以考虑算 \(f(i)\) 的时候再用相同的手法枚举左侧和它贴在一起的人的匹配到的最小的 \(b_j\)。所以都是基于不动的那个人来枚举转移的。

还需要注意有些人初始就会连在一块的细节。时间复杂度 \(O(nm)\),还是比较好写的。

代码

USACO20JAN Falling Portals

很妙的题!

不妨考虑 \(a_i\gt a_j\) 的情况,此时有一个贪心策略:当我们的人和另一个平台相遇的时候,如果这个平台下落的更快,我们就切换;否则就不切换。

这个策略的正确性何以保证,会不会出现,不换反而更快完成目标的情况?我们可以画出每个平台的位置-时间图像。那么我们相当于是在沿着时间轴前进,当两条直线相交的时候,可以切换所在直线。当画出这个图以后,就会发现下降最快的路线就是上述的贪心过程。

而且发现一点:在这个切换过程中,我们所位于的直线:它的 \(a_i\) (截距)一定是不断增加的,而 \(i\) (斜率)一定也是不断增加的。

因此我们把所有人按照 \(a\) 排序,然后对于 \(i\),我们依次考虑它后面的每个人,如果能切换,就切换。这就是我们的行走路径,它显然构成一个凸壳的形状。

怎么快速维护呢?可以按照 \(a\) 从大到小加入,然后加入一条直线的时候,就是删去了凸壳最左侧的若干线段,可以直接用单调栈维护。

当我们得到凸壳以后,求答案就很容易了:直接在凸壳上二分走的远的一条直线,使得我们切换到这条直线 \(l\) 的时候,依然位于 \(j\) 上方,也就等价于,切换到 \(l\) 的时刻,不晚于 \(l\)\(j\) 相遇的时刻。然后整个问题在 \(O(n\log n)\) 的时间内解决。

代码

CF1379F2 Chess Strikes Back (hard version)

一道很不错的 div2F。

首先来研究一下,所有白格都可用的答案最大值:发现其实就是 \(n\times m\)

我们把棋盘划分成若干 \(2\times 2\) 的方格,不难发现,每个方格内,都要在两个白格子中选一个(左上的 or 右下的)。

那么,我们的一个构造就是:每一个 \(2\times 2\) 都选择右下的那个格子,这个显然是合法的。

如果有一个 \(2\times 2\) 选择了左上的格子:那么不难发现其所有左上方的 \(2\times 2\) 都必须跟着选左上。

因此,我们考虑把 \(2\times 2\) 视作基本单位,然后得到一个 \(n\times m\) 的矩形,令不能选左上的为黑点,不能选右下的为白点(一个点可以具有两种属性,也可以都不具备)。则合法当且仅当:

  • 对于每个白点,不存在一个位于左上方的黑点(位置和其重合也视作位于左上)

我们考虑实时维护点对 \((x,y)\) 的个数 \(cnt\),使得 \(x\) 为黑点,\(y\) 为白点,且 \(x\) 位于 \(y\) 左上。则合法等价于 \(cnt=0\)

每一轮修改相当于是有 \(O(1)\) 次加入/删除某个白点/黑点。如果是白点,我们就要查询其左上角的黑点个数;如果是黑点,我们就要查询其右下角的白点个数。

在线做可以树套树解决,当然我们不需要这么麻烦,因为能离线所以直接 cdq 分治就可以了。

时间复杂度 \(O(q\log^2)\)

代码

APIO2019 路灯

这个时候的 APIO 还很清新啊啊啊。

这个问的东西第一眼看上去还挺难维护,所以去想了分块 bitset 什么的,但是后来感觉走远了。。。回来继续思考 polylog 做法。

然后发现我们点亮/熄灭一个点的时候,变得合法/不合法的区间,可以被表示成二维平面上的一个区间。

所以相当于是,我们每次点亮/熄灭一个矩形范围内的点,然后查询是,对于一个单点,查询 \(\le i\) 的时刻内,其有多少时刻是亮着的。

这个可以维护一下关于 \(i\) 的一次函数,然后就是矩形加,单点求值。一样在线树套树离线 cdq 带走了,非常舒服。

时间复杂度 \(O(q\log^2)\)

代码

CF1671F Permutation Counting

质量很高的 dp。

首先,数据范围非常引人注目:多组数据,\(n\lt 998244353\)\(k,x\le 11\)。种种迹象表明这是一个预处理某些内容后,再快速计算的问题,且大概率预处理过程和 \(n\) 无关。

考虑 \(k,x\le 11\),意味着这个排列里,绝大部分位置都应该满足 \(p_i=i\)。更具体的:假设 \(i\) 这个人,最后移动到了 \(j\)。则我们把 \([i,j]\)(或 \([j,i]\))全部涂上黑色。那么,整个排列的黑色位置就不会太多。

一个很自然的想法是:我们枚举黑色连续段的长度和 \(s\) 和其数量 \(c\),则我们只需要把剩余的 \(n-s\) 个人分配给 \(c+1\) 个空,这是经典的插板。这个想法非常契合数据范围,那么我们就需要预处理 \(G(s,c)[a,b]\) 表示,长度和为 \(s\),分成 \(c\) 段,有 \(a\) 个逆序,\(b\) 个下降的方案数。

当求出 \(G\) 以后,我们做一次背包就完成了预处理。

先来分析一下变量的上界:\(a,b\) 的上界很明显就是 \(k\),但是 \(s,c\) 呢?由于每一个黑色段至少贡献 \(1\) 的逆序对,显然 \(c\) 的上界也是 \(k\);而 \(s\) 可以达到 \(2k\)\(k\) 组长度为 \(2\) 的拼一起就行了。

现在考虑,由于有一个“所有位置都是黑色”这样一个限制难以处理,我们考虑容斥掉:不妨钦定集合 \(S\) 中的位置是白色,那么就把整个长度为 \(s\) 的序列划分成了 \(|S|+1\) 个独立的段,对于这些段,我们并不要求每个位置都是黑色的,只对其逆序和下降有要求。因此我们设 \(F(s)[a,b]\) 是长度为 \(s\) 的排列,有多少个满足逆序为 \(a\),下降为 \(b\)。知道了 \(F\) 以后,\(G\) 容易通过背包求出。

至于求 \(F\),从小到大加数的方法不太行得通,因为我们还有对下降的限制。考虑可以直接从前往后状压。但是注意,\(s\) 的上界是 \(2k\),但是 \(2^{2k}\) 的复杂度就炸了。

注意到一个事情:就是,对于一个黑色连续段,如果对于某处 \((i,i+1)\),没有一对交换跨越他们(也就是这里劈开,两边的值域不交),那么我们可以从这里断开,这样形成的若干段,我们称为”基本连续段“。显然每一个黑色连续段存在唯一的基本连续段划分,而基本连续段的长度 \(\le k+1\),因为长度为 \(len\) 的基本连续段至少有 \(len+1\) 个逆序对。

所以,我们求 \(F(s)[a,b]\) 的时候,只用对 \(s\le k+1\) 的求,并且我们在这部分状压 dp 的时候,只计算基本连续段的方案数。然后在 \(G\) 的背包 dp 这里,我们枚举拼接上去的一段长度,这个时候长度也应该满足 \(\le k+1\)

然后三个 dp 按部就班写出来即可。

代码

CF1896F Bracket Xoring

神题啊,怎么大家都杀的这么快????

按套路先猜找一下无解的充要条件,发现奇数个 \(1\) 肯定死,因为这个操作的 \(1\) 肯定是成对出现的,这个很简单。

然后玩了一会儿发现不对啊,\(1010\) 也无解啊??

进一步发现 \(s_1\neq s_{2n}\) 也一定无解,因为,每次操作这两人因为在最外面缘故都会 xor \(1\),所以他们的相等关系不会变。

那我们猜一下,这两个条件都满足就一定有解了,怎么构造?

(()) 这哥们带来的是 1001(()()) 这哥们带来的是 100001

也就是说,我们可以用一次操作做到让 \(s_1=s_{2},s_{3}=s_{4},s_{5}=s_{6},...\) 这样子!只需要考察所有的 \(s_{2i-1}\neq s_{2i}\)\((2i-1,2i)\)。它们一定有偶数个,那么两两配对以后,就 (()()....()) 就可以了。

但是这样好像还是做不了啊?我们发现没用到 \(s_1=s_{2n}\) 的性质。

考虑这样一个事情!我们扔掉 \(s_1\)\(s_{2n}\)(它们套在最外面,一个 ( 另一个 ) 即可),然后用刚才的方法让 \(s_2=s_3,s_4=s_5,s_6=s_7\),也就是 \(2i\) 去和 \(2i+1\) 相等。

这样相比于刚才有一个什么好处?就是我们可以让每个 \((2i,2i+1)\) 在下一次操作后都变成 \(0\) 了:如果他们两个都是 \(1\) 我们就塞 ()

,否则塞 )(。由于最外层是有一个 () 的,所以括号始终都是合法的!

那么如果两次操作完,\(s_1\)\(s_{2n}\)\(1\) 的话,再来一次 (()()()...()) 就好了。

这样我们就在三次操作内构造出了答案。

代码

QOJ7303 City United

非常非常精妙的题!

\(\alpha=13\),对于一个非空子集,假设其导出子图有 \(k\) 个连通块,我们肯定希望其贡献为 \([k=1]\),然而这太难做到,直接暴力 dp 只能得到一个 \(O(bell(\alpha))\) 级别的做法。那么这显然没有利用到只关注答案奇偶性的性质。

一个很高明的点是:\(2^k\)\(k\ge 2\) 的时候,\(\bmod 4=0\)。所以我们不妨对所有非空子集的 \(2^k\) 求和,而不是对 \([k=1]\) 求和(在 \(\bmod 4\) 意义下)。然后最后看是 \(0\) 还是 \(2\) 就行了(分别对应答案为 \(0/1\))。

\(2^k\) 有很好的组合意义性质:相当于给选出的点集染色,使得一个连通块的颜色全部相同,这样的染色方案数。

然后就暴力地在 \(O(3^\alpha\times n)\) 的时间内解决了。

代码

ARC168F Up-Down Queries

真是神题。

首先考虑到 \(y\) 序列永远是递增的,因此可以类似 slope trick 地,维护一个值域为 \([0,m]\) 的多重集 \(S\)。如果有 \(k\)\(i\),就表示 \(y_{i+1}=y_i+k\)\(y_0=0\))。注意到一次操作就是先加入两个 \(x\) 再删除最小元。而注意到答案等于 \(\sum_{u\in S} (m-u)\),而 \(n\) 次操作后显然 \(|S|=n\),所以我们就只关心 \(\sum_{u\in S}u\) 了。这一步转化是非常重要的,因为这个问题可以被描述成一个费用流问题:

建立 \(n\) 个点,然后 \(s\rightarrow i\) 容量为 \(2\),费用为 \(x_i\)\(i\rightarrow t\) 容量为 \(1\),费用为 \(0\)\(i\rightarrow i+1\) 容量为 \(\infty\),费用为 \(0\)。然后 \(2\times \sum x\) 减去最小费用最大流就是 \(\sum_{u\in S}u\)。显然,这个费用流是在计算每一轮删除的元素和。并且很显然,\(i\rightarrow t\) 这里出去的流量,应该就是 \(\le i\) 的剩下的人里 \(x_i\) 最小的。

很自然地,考虑模拟费用流加速这个问题(毕竟我们已经知道解这个费用流可以贪心 \(O(n\log n)\) 做到)。注意到一次修改就是修改了 \(s\rightarrow x\) 这一条边的费用,那么由于已经是满流了,我们就考虑消圈。

首先负权圈的形式很简单:一定是 \(s\rightarrow u\rightarrow v\rightarrow s\)(这里可以有 \(u\gt v\),意味着走了反边),且显然 \(u,v\) 恰有一者 \(=x\)。否则这次消圈就和我们的修改无关。而且一个很棒的事情是不会同时出现 \(s\rightarrow x\)\(x\rightarrow s\) 的两种圈,不然这两个圈拼一起得到的结果就和 \(x\) 无关了。所以我们要么让 \(s\rightarrow x\) 的流量始终变大(至多从 \(0\) 变为 \(2\)),要么让其始终减小(至多从 \(2\) 变成 \(0\))。也就是说只有 \(O(1)\) 次消圈过程。所以只要我们能快速实现消圈就好了。

一个问题是,会不会我们增广完 \(s\rightarrow x\rightarrow y\rightarrow s\) 这个圈以后,又出现了一个新的负权圈 \(s\rightarrow y\rightarrow z\rightarrow s\)?如果是这样我们直接拼 \(s\rightarrow x\rightarrow z\rightarrow s\) 就好了。

所以说,我们每次消圈的时候,首先暴力枚举 \(u=x/v=x\) 两种情况。以 \(u=x\) 为例,我们就需要讨论 \(x\gt y\)\(x\lt y\) 的情况。注意到 \(x\gt y\) 的话,我们是在走反边,所以要求 \(y\sim x\) 这一段是有流量的,那么就是一个线段树二分的事情。其余情况同理。

在分析明白上面的过程后,消圈的维护就可以用比较简单的线段树来维护,又因为每轮只增广 \(O(1)\) 次,所以时间复杂度即为 \(O((n+q)\log)\)

代码

posted on 2023-11-05 19:55  Cry_For_theMoon  阅读(299)  评论(4编辑  收藏  举报