2024 联合省选 题解
D1T1 季风
考虑要求 \(\begin{cases}\sum\limits_{i=0}^{m-1}(x'_i+x_{i\bmod n})=x\\ \sum\limits_{i=0}^{m-1}(y'_i+y_{i\bmod n})=y \\ |x'_i|+|y'_i|\le k \end{cases}\) 发现其等价于 \(|x-\sum\limits_{i=0}^{m-1}x_{i\bmod n}|+|y-\sum\limits_{i=0}^{m-1}y_{i\bmod n}|\le mk\)。
具体的,就是在后式左部里面和 \(0\) 的差距都可以用 \(x'_i\) 或者 \(y'_i\) 补齐。
按照 \(m\bmod n\) 划分成若干个等价类,对于 \(i\in [0,n)\cap\mathbb{Z}\),我们记 \(X_0=x-\sum\limits_{j=0}^ix_j\),\(Y_0=y-\sum\limits_{j=0}^iy_j\),\(X=\sum\limits_{i=0}^{n-1}x_i\),\(Y=\sum\limits_{i=0}^{n-1}y_i\)。
我们就是想要找到最小的 \(g\),使得 \(|X_0-Xg|+|Y_0-Yg|\le k(i+1)+kng\)。
按照两个绝对值的取值将 \(g\) 划分成 \(1\sim 3\) 个值域,每一个值域都是形如 \(kg+b\le 0\) 的方程,可以直接 \(O(1)\) 计算。
需要注意边界情况。时间复杂度 \(O(n)\)。
D1T2 魔法手杖
首先考虑只有异或怎么做。
我们建出 01Trie,从上往下逐位考虑这一位能不能为 \(1\)。
如果这个节点只有 0 儿子或者 1 儿子,则这一位可以选 1,递归到有儿子的那一边。
如果这个节点同时有 0 儿子和 1 儿子,则这一位只能为 0,根据 \(x\) 的选择分别递归到 0 儿子和 1 儿子。
这样的时间复杂度和遍历一遍是一致的 \(O(nk)\)。
考虑现在可以有加法,我们考虑魔改一下上面的过程。
因为有了加法的限制,所以在有两个儿子的时候不一定能为 0,在只有一个儿子的时候不一定能为 1。而对于用加法的部分,我们只关注其中的最小值,所以我们只需要让 \(min+\max(x)\ge ans\),我们就可以做出对应的选择,其中 \(\max(x)\) 表示可能最大的 \(x\),具体的,可以通过一系列分类讨论得到答案。
void get_ans(int pos,__int128 ans,__int128 x,__int128 minn,int las,int nw)
{
if(nw==-1) return ::ans=max(::ans,ans),void();
if(s[pos].son0&&s[pos].son1)
{
//try go to son0
if(las>=s[s[pos].son1].sum&&min(minn,s[s[pos].son1].minn)+x+pw[nw+1]-1>=ans+pw[nw])
get_ans(s[pos].son0,ans+pw[nw],x+pw[nw],min(minn,s[s[pos].son1].minn),las-s[s[pos].son1].sum,nw-1);
else
{
if(las>=s[s[pos].son1].sum) ::ans=max(::ans,min(minn,s[s[pos].son1].minn)+x+pw[nw+1]-1);
get_ans(s[pos].son0,ans,x,minn,las,nw-1);
}
//try go to son1
if(las>=s[s[pos].son0].sum&&min(minn,s[s[pos].son0].minn)+x+pw[nw]-1>=ans+pw[nw])
get_ans(s[pos].son1,ans+pw[nw],x,min(minn,s[s[pos].son0].minn),las-s[s[pos].son0].sum,nw-1);
else
{
if(las>=s[s[pos].son0].sum) ::ans=max(::ans,min(minn,s[s[pos].son0].minn)+x+pw[nw]-1);
get_ans(s[pos].son1,ans,x+pw[nw],minn,las,nw-1);
}
}
else if(s[pos].son0)
{
if(minn+x+pw[nw+1]-1>=ans+pw[nw]) get_ans(s[pos].son0,ans+pw[nw],x+pw[nw],minn,las,nw-1);
else ::ans=max(::ans,minn+x+pw[nw+1]-1);
}
else
{
if(minn+x+pw[nw]-1>=ans+pw[nw]) get_ans(s[pos].son1,ans+pw[nw],x,minn,las,nw-1);
else ::ans=max(::ans,minn+x+pw[nw]-1),get_ans(s[pos].son1,ans,x+pw[nw],minn,las,nw-1);
}
}
D1T3 虫洞
来自 Wuyanru 的做法,感觉很厉害。
接下来的联通指的是原图的弱联通,但是实际上也就是强连通。
首先从简单的地方开始考虑,假设原图 \(m=1\),现在要加入第 \(2\) 种颜色的边的过程。
我们考虑两个连通块 \(G_1\) 和 \(G_2\)(显然这两个连通块是环),以及 \(u\in G_1\),\(v\in G_2\),如果我们连接了 \(u\to v\) 颜色为 \(2\) 的边,我们可以将 \(u\) 和 \(v\) 同时按照颜色 \(1\) 的边移动,我们就得到了新的一条边。
不断重复这个过程直到回到 \(u\to v\),我们就得到了 \(G_1\Rightarrow G_2\) 的边,由于需要满足限制,显然会有 \(|G_1|=|G_2|\)。
现在拓展到初始有 \(m\ge 1\) 种颜色的情况。
我们想要知道,什么样的 \(G_1\) 和 \(G_2\) 之间是可以连边的(可能 \(G_1\) 和 \(G_2\) 是同一个连通块)。
假设我们选择连接了边 \(u\to v\),则按照任何颜色向任何方向移动到达的点都是对应相等的,不难发现,这种情况下 \(G_1\) 和 \(G_2\) 就是同构的,也就是存在一种重标号的方式,使得两张图的边集相同,同时 \(G_1\) 中 \(u\) 节点和 \(G_2\) 中 \(v\) 节点对应同一标号。
可以证明这种同构是由传递性的,同时如果 \(G_1\) 和 \(G_2\) 对于某一组 \((u,v)\) 同构,则对于任意的 \((u,v)\) 均同构。
也就意味着,我们将原图中所有的连通块可以按照同构关系划分成若干等价类,只有同一等价类内的边才能够相连。
现在可以在某一个等价类内部计算答案,然后将所有的答案乘在一起即可。
设计状态 \(dp_{i,j,S}\) 表示对于大小为 \(S\) 的连通块,一共有 \(i\) 个,连接 \(j\) 条边变成一个连通块的连边方案数。如果可以计算出这个值,我们就可以通过一个简单 DP 来计算出所有可能的方案。
初始状态有 \(dp_{i,0,S}=[i=1]\)。
转移,考虑连的第一组边,由于最后会连成一个连通块,所以这一次连完之后得到的连通块也都是同构的,具体的,我们分成了 \(x(x|i)\) 组,则划分的方案数 \(f_{i,x}=\dfrac{i!}{x!\left(\dfrac{i}{x}\right)^x}\),也就是将 \(i\) 分成 \(x\) 个大小为 \(\dfrac{i}{x}\) 的环排列的方案数。
考虑连边,对于第一个大连通块,其中的每一个连通块都有向下一个连通块选择的权力,方案数为 \(S^{\frac{i}{x}}\),而对于其后的每一组,我们确定了前 \(\dfrac{i}{x}-1\) 个向下一个联通块的连边之后,最后一条连边也就随之确定了,所以方案数为 \(S^{\frac{i}{x}-1}\),所以总体的方案数为 \(S^{i-x+1}\)。
由此可以得到转移 \(dp_{i,j,S}=\sum\limits_{x|i}dp_{x,j-1,\frac{iS}{x}}f_{i,x}S^{i-x+1}\)。
这样的 DP 状态量是 \(O(n^2k)\) 的,转移复杂度是 \(O(n^2k\ln n)\) 的,无法接受,考虑优化。
我们发现 \(S\) 在整个 DP 转移中的参与是很少的,所以我们考虑优化掉它。
结论:\(dp_{i,j,S}=dp_{i,j,1}S^{i+j-1}\)。
证明:考虑使用数学归纳法证明。
对于 \(j=0\) 显然成立。\[\begin{aligned}dp_{i,j,S}&=\sum\limits_{x|i}dp_{x,j-1,\frac{iS}{x}}f_{i,x}S^{i-x+1}\\ &=\sum\limits_{x|i}dp_{x,j-1,1}\left(\dfrac{iS}{x}\right)^{x+j-2}f_{i,x}S^{i-x+1}\\ &=S^{i+j-1}\sum\limits_{x|i}dp_{x,j-1,1}\left(\dfrac{i}{x}\right)^{x+j-2}f_{i,x}\\ &=S^{i+j-1}\sum\limits_{x|i}dp_{x,j-1,\frac{i}{x}}f_{i,x}\\ &=S^{i+j-1}dp_{i,j,1}\end{aligned}\]
因此,我们可以直接忽略掉 \(S\) 这一维,将 DP 简化成 \(dp_{i,j}\) 用来代指之前的 \(dp_{i,j,1}\) 即可。
那么现在的转移就是 \(dp_{i,j}=\sum\limits_{x|i}dp_{x,j-1}f_{i,x}\left(\dfrac{i}{x}\right)^{x+j-2}\)。
复杂度优化到了 \(O(nk\ln n)\),但是 \(k\) 还是过大了,考虑如何优化掉 \(k\)。
我们将 \(f_{i,x}\) 对应的式子带入,\(dp_{i,j}=\sum\limits_{x|i}dp_{x,j-1}\dfrac{i!}{x!\left(\dfrac{i}{x}\right)^x}\left(\dfrac{i}{x}\right)^{x+j-2}=\sum\limits_{x|i}dp_{x,j-1}\dfrac{i!}{x!}\left(\dfrac{i}{x}\right)^{j-2}\)。
记 \(dq_{i,j}=\dfrac{dp_{i,j}}{i!i^{j-2}}\),则可以得到转移 \(dq_{i,j}=\sum\limits_{x|i}\dfrac{dq_{x,j-1}}{x}\)。
考虑 \(dq_{i,j}\) 的组合意义:对于一个长度为 \(j\) 的序列 \(\{a\}\),满足 \(a_1=1\),\(a_k|a_{k+1},k=1,2,\dots j\),\(a_j|i\),它的权值等于 \(\prod\limits_{k=1}^j\dfrac{1}{a_k}\),求所有满足限制的序列的权值和。
有这个组合意义可以得到转移:\(dq_{i,x+y}=\sum\limits_{j|i}dq_{j,x}dq_{\frac{i}{j},y}\dfrac{1}{j^y}\)。由此我们可以使用倍增的方式得到 \(dq_{*,k}\) 的值,转移的时间复杂度为 \(O(n\ln n\log k)\)。
后面的计算贡献可以简单的做到 \(O(n^2)\)。
现在的问题就是如何划分等价类了。
由于如果两个图同构,则以任何一对 \(u,v\) 标号为 \(1\),都可以得到一种同构的方式。由此,我们只需要找到一种固定的重标号方式,使得同构的图最终的重标号方式相同,使得每一种颜色对应的排列时相同的,我们就可以使用哈希判断了。
我们只需要将每一个点的出边按照颜色从小到大遍历,按照 dfs 到的顺序标号即可,时间复杂度 \(O(nm)\)。
所以最终时间复杂度为 \(O(n^2+nm+n\ln n\log k)\)。
D2T1 迷宫守卫
考虑 Bob 的策略,如果他可以在某一个节选选择向左子树走还是向右子树走,左子树能够得到的字典序最小的字符串为 \(S_l\),右子树能够得到的字典序最小的字符串为 \(S_r\),则这个节点能够得到的字典序最小的字符串为 \(\min(S_lS_r,S_rS_l)\)。
我们记 \(f_{pos,x}\) 表示让 Bob 在 \(pos\) 节点是,能够走到的第一个节点的编号最大为 \(x\) 的最小代价。
· 如果 \(x\) 在 \(pos\) 的左子树,则 \(f_{pos,x}=f_{ls,x}+\min(w_{pos},\min\limits_{x'\ge x}f_{rs,x'})\)。
· 如果 \(x\) 在 \(pos\) 的右子树,则 \(f_{pos,x}=f_{rs,x}+\min\limits_{x'\ge x}f_{ls,x'}\)。
这个 DP 可以按照 \(x\) 从大到小 DP,复杂度为 \(O(2^nn)\)。
然后我们考虑确定答案,从上往下贪心,我们找到能够接受的最大的 \(x\),预留出来最小的在另一个子树的代价,然后递归到 \(x\) 对应的子树。
在回溯到这个节点的时候,如果我们如果可以不取 \(w_{pos}\),我们就不取,然后把预留出来的代价加回来,递归到另一个子树。
时间复杂度 \(O(2^nn)\),可能有一些细节。
D2T2 重塑时光
记 \(u\Rightarrow v\) 表示存在一条从 \(u\) 到 \(v\) 的路径。
我们考虑记 \(f_{i,S}\) 表示将 \(S\) 点集拆分成 \(i\) 个块,使得不存在两个联通块 \(S_1,S_2\),使得 \(\exists u_1\in S_1,u_2\in S_2,u_1\Rightarrow u_2\) 且 \(\exists v_1\in S_1,v_2\in S_2,v_1\Rightarrow v_2\),考虑块内拓扑序的情况下的方案数。
如果我们能够得到 \(f_{i,S}\),我们就容易计算出答案。
记 \(g_{S}\) 表示 \(S\) 点集合法的拓扑序数量。
记 \(h_{i,S}\) 表示将 \(S\) 点集拆分成 \(i\) 块,使得不存在两个联通块 \(S_1,S_2\),使得 \(\exists u_1\in S_1,u_2\in S_2,u_1\Rightarrow u_2\) 或 \(\exists v_1\in S_1,v_2\in S_2,v_1\Rightarrow v_2\),考虑块内拓扑序的情况下的方案数。
我们考虑如何得到这些数组。
对于 \(g\),我们有 \(g_{S}=\sum\limits_{a\in S}[\neg\exists u\in (S\setminus \{a\}),a\Rightarrow u]g_{S\setminus\{a\}}\)。
对于 \(h\),我们有 \(h_{i,S}=\dfrac{1}{i}\sum\limits_{T\subseteq S}[\neg(\exists u_1\in T,u_2\in (S\setminus T),u_1\Rightarrow u_2\lor \exists v_1\in T,v_2\in (S\setminus T),v_2\Rightarrow v_1)]h_{i-1,S\setminus T}g_T\)。
对于 \(f\),我们考虑直接枚举连通块会出现什么问题:对于某两个没有偏序关系的连通块,它们之间的顺序是不重要的,所以逐个枚举分成的点集会出现重复统计的问题。
考虑容斥,我们一次不是加入一个点集,而是若干个两两没有偏序关系的点集(也就是数组 \(h\)),如果这次加入了 \(j\) 个点集,它的容斥系数就是 \((-1)^{j-1}\)。
发现 \(\sum\limits_{i=1}^n\dbinom{n}{i}(-1)^{i-1}=1\),刚好可以解决重复统计的问题。
具体的,转移就是 \(f_{i,S}=\sum\limits_{T\subseteq S}\sum\limits_{j=1}^i[\neg\exists u\in T,v\in (S\setminus T),u\Rightarrow v](-1)^{j-1}f_{i-j,S\setminus T}h_{j,T}\)。
最后考虑统计答案,我们可以将 \(t\)个点集插到 \(k\) 次分割线之间,首先分割线的排列方式有 \(k!\) 种,然后考虑第 \(i\) 个插入的点集有 \(k+2-i\) 个位置可选,所以方案数为 \(k!\times \prod\limits_{i=1}^t(k+2-i)\)。
最后除以总方案数 \((n+k)!\) 即可。
上面所有的艾佛森括号都可以通过 \(O(1)\) 次位运算得到,总体复杂度 \(O(3^nn^2)\)。
然而这个做法并不能稳定通过。
记 \(h'_{i,S}=(-1)^{i-1}h_{i,S}\)。我们考虑对于可以做贡献的 \(S\) 和 \(T\),做出贡献的形式时 \(f_{i+j,S}\gets f_{i,S\setminus T}h'_{j,T}\)。
我们记 \(F_S=\sum\limits_{i=0}^nf_{i,S}x^i\),\(H_S=\sum\limits_{i=0}^nh'_{i,S}x^i\)。
则有 \(F_{S}\gets F_{S\setminus T}H_{T}\)。显然,\(f_{i,S}\) 或 \(h'_{i,S}\) 值非零的必要条件时 \(i\le |S|\),所以多项式并不会溢出,所以我们可以直接通过取 \(F\) 和 \(H\) 的 \(n+1\) 处点值进行运算,最后再通过这些点值来得到 \(F_{U}\)。有一点类似于 NTT 优化卷积。
这样时间复杂度就优化到了 \(O(n3^n)\)。
D2T3 最长待机
发现 Sleep++ 的所有函数构成了一棵以 \(1\) 为根的树,可以在 \(e=1\) 的节点选择遍历子树若干次,而 \(e=0\) 的节点能且只能将子树遍历 1 次。
首先我们考虑什么情况下游戏才是公平的。
第一种情况就是双方必然平局,这种情况只会在双方都不能执行操作的情况下发生。否则,只要有一方可以进行操作,则他必然可以保证不必然平局,对于某种平局的状态,他可以增加他的某一次操作,结束时间就不同了。
第二种情况就是双方无法确定谁能赢,这种情况至少需要必然出现一次同时进行决策。只有在同时决策的情况下,才有可能出现不能确定的情况,否则靠后决策的一方完全可以通过决策来让自己的结束时间延后。
在第二种情况下,我们认为这样的同时决策必然是第一次决策。如果不是,在这一次决策之前的分析可以套用第一种情况,就不是必然同时操作了。
但是“第一次决策是同时决策”这个条件并不充分,例如如果小 \(\omega\) 在第一次决策之后还能有第二次决策,而小 \(\aleph\) 没有,则小 \(\omega\) 可以在第二次操作的时候超过小 \(\aleph\),无论第一次操作的时候谁的时间更长。
也就意味着,两个人在第一次同时决策之后,后面的决策应该出现一个“轮流超越”知道双方都没有剩余的决策。这样才能够使得第一次决定的胜负能够保留到最后。
这里的轮流超越可以举一个例子:例如小 \(\omega\) 和小 \(\aleph\) 拿到的都是如下图的一棵树。
假设第一次在 \(2\) 号点,小 \(\omega\) 输入的数比小 \(\aleph\) 输入的数大;则小 \(\aleph\) 优先进入 3 号点的决策,他会选择一个比小 \(\omega\) 到下一次决策前的等待时间要大的数(否则相当于没有在 3 号点进行决策,必然不优);然后小 \(\omega\) 再进入 3 号点的决策,选择一个比小 \(\aleph\) 到下一次决策前的等待时间要大的数;4 号点是类似的,最后小 \(\omega\) 的程序运行时间更长。
反之,小 \(\aleph\) 在第一次输入的时候如果比小 \(\omega\) 要大,则是小 \(\aleph\) 获胜。
所以这样的游戏就是公平的。
现在我们需要分析怎样的决策之间是能够实现超越的:
我们记一个 \(e=1\) 的决策点的决策等级 \(T\) 为其所有子孙节点中 \(e=1\) 的决策点的决策等级(若没有这样的子孙就为 \(0\))的最大值 \(+1\)。
例如对于下面这棵树,2 号点和 4 号点的决策等级为 \(1\),3 号点的决策等级为 \(2\)。
由此我们可以得到如下结论:
- 相同等级的决策点等价.
- 一个决策点可以在先操作的情况下超越任意个操作等级比它小的决策点。
- 一个决策点可以在后操作的情况下超越一个操作等级等于它的决策点后面接上任意个操作等级比它小的决策点。
证明使用数学归纳法:
在 \(T=1\) 的时候显然成立。
假设对于 \(T<k\) 成立,我们尝试证明在 \(T=k\) 的时候成立
对于第一个结论,它可以根据输入变成任意量级的 \(k-1\) 级决策点,因为 \(k-1\) 级决策点两两等价,所以 \(k\) 级决策点两两等价。
对于第二个结论,输入时至少输入 \(n+O(1)\),其中 \(n\) 为比它小的决策点的数量,也就意味着我们是至少 \(n+O(1)\) 个 \(k-1\) 级的决策点和对面的 \(n\) 的 \(< k\) 级的决策点,显然是可以超越的。
对于第三个结论,由于是对面先操作,所以他操作完之后就被拆分成了若干操作等级 \(< k\) 的决策点,和第二个结论等价。
我们考虑按照遍历顺序把小 \(\omega\) 和小 \(\aleph\) 遇到的决策点等级序列拉出来。因为三结论,所以一个决策点的前面有更大的决策点,这个决策点就是没有意义的。
这也就意味着我们是需要取出这样个序列的前缀最大值(非严格),只有这些值才是有意义的,则游戏公平的充要条件就是:第一次决策同时进行,且对应的决策序列相同。
例如对于下图,得到的决策序列就是 \(1,2,1\),而有意义的决策序列就是 \(1,2\)。
这也就意味着我们只需要求出第一次决策开始的时间 \(tim\),以及决策序列 \(a_1,a_2 \dots a_k\),即可得到小 \(\aleph\) 对应的答案:\(tim+\sum\limits_{i=1}^ka_i+[tim+k\ge 2]\)。
其中第一项代表前面等待的时间对应的节点数,第二项代表对应的决策序列的每一个决策点的最小的树的大小之和,第三项代表如果前面想生成的是一片森林,我们就需要一个根节点将其连接起来。
如果每一次 \(O(n)\) 暴力求解,可以做到 \(O(qn)\),可以获得 \(24\) 分的成绩。
如果进行一定的剪枝优化,可以冲过去一些点,得到 \(48\) 分。
现在我们想知道如何快速维护这些信息。
对于前面需要等待的时间,我们只需要知道子树内第一个决策点之前有多少个叶子节点即可,将树拍成 dfn 序,这个是可以用数据结构 \(O(n+m\log n)\) 维护的。
重点是如何求出 \(k\) 以及 \(\sum\limits_{i=1}^ka_i\)。
考虑到如果直接暴力维护我们上面设计的“决策等级”,需要树剖+二分之类的东西,但是这样会破坏了游戏过程中遍历的顺序,我们就不能够通过我们维护的序列直接提取前缀最大值序列了。
所以我们想要在不破坏 dfn 序的情况下维护一个能够代替“决策等级”的信息。
由于不破坏 dfn 序,所以我们所有的修改只能够基于子树,不能基于链了。
我们记 \(val_i\) 表示 \(i\) 节点的父亲到根的路径上的 \(e=1\) 的节点的数量,\([L_i,R_i]\) 为 \(i\) 的子树对应 dfn 序上的区间。则一个节点的决策等级 \(T=\max\limits_{j=L_i}^{R_i}val_j-val_i\)。
而每一次修改 \(e\),改变的 \(val\) 都是一次区间 \(\pm 1\),所以是很好维护的。
现在再来考虑求解。
对于 \(e_k=1\) 的情况,我们可以直接求出决策等级。
对于 \(e_k=0\) 的情况,发现所有 \(val_i=val_k\) 的点在 \(k\) 的子树内形成了一个包含根的连通块,而这个连通块内所有 \(e_i=1\) 的节点都是这个连通块的叶子。
这也就意味着,我们按照 \(val_k\) 分割 \([L_k,R_k]\) 这段序列,我们得到的每一段刚好对应一个决策点,而且这个决策点对应的决策等级就是这段区间的最大值 \(-val_k\)。
由于 \(val_k\) 必然是 \([L_k,R_k]\) 中 \(val\) 的最小值,所以我们在线段树的每一个节点维护其对应区间以区间最小值 \(min\) 分割得到的序列的区间的区间最大值序列的前缀最大值序列的长度以及权值和。
具体的,如果所有区间的最小值位置形如 \(\dots min \dots min \dots min \dots\),我们忽略掉第一个 \(min\) 的前面以及最后一个 \(min\) 的后面这两个区间,改成维护这样个 \(min\) 的位置。
考虑如何合并两个区间的信息,如果这样个区间的最小值不同,可以直接保留最小值更小的一遍;否则,左部是不会受到右部的影响的,中间需要单独加入左部分最后一个 \(min\) 和右部分第一个 \(min\) 之间的区间,而对于右部分,我们只保留其大于等于左部序列最大值的一段后缀。具体的,这个部分和楼房重建类似,可以使用兔队线段树来维护。
实现过程中可能会有一些细节:例如由于定义的 \(val\) 是从父亲到根的,所以需要给每一个叶子加一个儿子,这样不影响答案且更方便处理,唯一的问题是会因此多一个 \(2\) 倍的常数,可能需要稍微卡常。
最终时间复杂度为 \(O(n\log n+m\log^2n)\)。