Cry_For_theMoon  

时间过得好快......

1. Fun Game

首先我们考虑一个链怎么做。

显然有个想法是一个一个接字符串,每次接的时候算一下最长的重叠位置(使用 kmp)即可。

但这样在 \(a\) 完全包含 \(b\) 的时候会出问题。

如果 \(a\) 完全包含 \(b\) 不难发现可以直接去掉 \(b\),当字符串两两并不完全包含的情况下这个想法是正确的。

那么可以设 \(dp(mask,i,0/1)\) 表示拼接了集合 mask 内的串,末尾是第 \(i\) 个串,且是正着还是倒着。

考虑上环:去掉包含关系以后,显然末尾只会和开头匹配:特判一下 \(n=1\) 的 corner 即可。

预处理复杂度 \(O(n^2\times |S|)\),dp部分复杂度 \(O(2^n\times n^2)\)

记录

2. Buffed Buffet

感觉就是纯纯二合一。

考虑只有离散食物如何做。

直接 dp:设 \(f(i,j)\) 表示前 \(i\) 个选出的重量为 \(j\) 能获得的最大价值和。

显然是一个 \(n\times W^2\) 的暴力存在的。这是个多重背包所以考虑按照模 \(w_i\) 分组,然后发现是个斜率优化的形式,就在 \(O(n\times W)\) 的时间内解决了。

考虑只有连续食物如何做。

我们知道分数背包是可以贪心的。这个式子很有趣,不妨把他看成加速度运动的位移公式:\(x=v_0t+\frac{1}{2}at^2\),然后时间就是 \(w\),初速度就是 \(t0\),加速度就是 \(-\Delta t\)。然后画出 \(v-t\) 图像,那么如果我们把图像分成非常多的宽度很小的段,那么每一段的函数都可以视作是常值,价值就是高度。

这样的话我们一定会每次选择当前高度最高的那个食物,然后跳到它的下一个小段。

然后当你选择到某个时刻就会出现一个食物和另一个食物高度相同的情况,此时应该把他们视作一个食物:设一个食物的加速度为 \(-a\),第二个加速度为 \(-b\),那么假设这两个食物拢共吃了 \(w\),最后他们结束的时候高度还是要相同,又因为初始高度相同,也就是有:\(a\times w_a=b\times w_b\) 存在。所以 \(w_a=\frac{b}{a+b}\times w\)\(w_b=\frac{a}{a+b}\times w\)。然后把 \(w_a,w_b\) 分别代入两个食物,然后相加,我们会发现新的加速度变成了 \(\frac{ab}{a+b}\)

特判一下加速度为 \(0\) 的情况即可,那么用优先队列维护高度最高的食物就可以做到 \(O(n\log n)\)

把两部分结合起来,就可以做到\(O(nW+nW\log n)\) 了。

记录

3. Graph Subpaths(来源:ByteDance/Moscow Workshops Programming Camp 2020 Online Contest, G;目前暂未发现公开提交地址)

给出一个拓扑序为 \(1,2,3,...,n\) 的 DAG,共 \(m\) 条边。然后给出 \(k\) 条路径。

对每个 \(i\in [2,n]\) 计数:\(1\rightarrow i\) 的路径,保证给出的 \(k\) 条路径都不是该路径的子串,模 \(998244353\)

\(L\)\(k\) 条路径的总长,保证 \(n,m,L\le 10^5\)


首先这题的代码并不难写,但是思想非常的精妙。

有一个暴力是把 \(k\) 条路径视作字符串建出 ACAM,然后因为字符集很大所以还要用到可持久化 seg 来辅助构建:然后设 \(dp(i,j)\) 是表示 \(1\rightarrow i\) 且落在 ACAM 的节点 \(j\) 上的路径数,则可以做到 \(O(nL\log n)\)。这样做没有什么进一步的改进余地了。

我们考虑把路径建出 trie:就是说建出一个 trie,然后叶子是 \(1\),每条从一个叶子到根 \(u\) 的路径,都是 DAG 上实际存在的一条 \(1\rightarrow u\) 的路径。

我们直观的感觉是路径数量就很多了,这样过于暴力,甚至无法存下 trie。

但是注意 \(u\rightarrow v\) 转移的时候,就是把 \(u\) 为根的 trie 复制一份,接在 \(v\) 的 trie 的根下面,这启发我们也许可以利用一些可持久化的数据结构:事实上如果 \(k=0\),那么我们直接接就好了,更进一步地,你发现既然我们只关注树内叶子的个数(对应路径数目),那么甚至并不需要建出树,维护一下 \(sz_u\) 即可,这本质上就是 \(k=0\) 的时候拓扑地 dp。在这里因为字符集很大,还是不能直接暴力建 trie,要用动态开点线段树来维护每个点的儿子。(不用 map 的理由是,我们后续将会进行 trie 的复制,显然我们不太能把 map 简单地浅复制完事,但如果我们的“map”也是个主席树,那么只需要把根节点复制一份就达到了目的)。

如果一些限制的路径,假设他是 \(x\rightarrow ... \rightarrow y\),我们在得到了 \(y\) 为根的 trie 以后,暴力沿着这条路径走,走到 \(x\) 对应的节点,然后我们要把 \(x\) 为根的子树全部删除。注意这个过程中为了保证后续操作的正确性,需要把沿途路径上的所有节点都复制一遍,这是可持久化的常用手段:而注意到复制节点的总数是 \(O(L)\) 的。

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

记录

4. Giant Penguin

首先思考 \(k=0\) 怎么做,树剖和点分树都是可以解决的,但树剖似乎不是很能进一步拓展。

考虑点分树,由于建完点分树以后树的原先结构被打乱,所以我们不一定用建 dfs 树/bfs 树之类的东西(可能会想去利用 dfs 树只有返祖边的一些性质),随便找一棵生成树后构建它的点分树即可。

然后点分治/点分树的精髓步骤在于:给出一个点 \(u\),然后考虑 \(u\) 的所有子树,我们处理所有路径端点在两颗子树中的情况。

值得一套的是,如果原图是树,那么点分树有这样一个性质:就是说虽然树的结构被打乱,但是 \(u\rightarrow v\) 的路径上,一定有一点是点分树上 \(u,v\) 的 LCA,所以我们可以断言路径一定经过 LCA,枚举 LCA 处理即可。问题在于当这个图不是树的情况下,我们就不能保证两条路径一定经过 \(LCA\)

但注意到跨越子树的边只有不超过 \(k\) 条,因为每条跨越子树的边都会给 \(u\) 带来一个简单环,把 \(u\),以及这些跨越边所涉及的点称为关键点:我们考虑 \(u\) 的两颗子树 \(x,y\),显然若 \(x\) 的一个点想要走到 \(y\) 中的另一个点,它不可能在不走到任何一个关键点的前提下达成目标,所以我们维护树内所有点到这些关键点的最短路即可。时间复杂度从原来的 \(O(n\log n)\) 变成了 \(O(nk\log n)\)(我们可能会想不是树怎么快速求最短路,事实上直接暴力 bfs 复杂度就是 \(nk\log n\) 了,原理还是点分树的树高特性)。

记录

总结:我们处理一类树,或者近似树的路径相关问题的时候,往往可以考虑点分(似乎今年 CSP T4 和去年省选 D2T1 都是可以点分的)。

5. 数据传输

\(k=1\) 是 ez 的,接下来我们考虑随机生成树的情况,也就是树高期望是 \(\log n\) 的。

那么我们可以把 \(u\rightarrow v\) 的路径抽出来看,我们发现 \(k=2\) 的时候不会出来这条链,\(k=3\) 的时候能跳到的点是一个近似毛毛虫的结构,而且如果你跳出了链,一定会跳到的是一个点的最小邻居。换言之有 \(2\times len\) 个点:\(len\) 个是链本身,另外 \(len\) 个对应了每个点的最小邻居。

这样我们就可以 dp 了:设 \(f(i,0/1)\) 是从起点走到 \(i\) 或者 \(i\) 的邻居的最小代价,就可以单次 \(O(k\log n)\) 处理询问,已经获得了 76pts 的恐怖分数(所以今年的部分分多少有点离谱了)。

考虑一般化的情况,朴素地想法是直接 ddp,我不太推荐直接莽,因为我冲完了沦为暴力同分......

有了上面的分析这个题目其实已经变的很明朗了,因为我们涉及到的对象是和两点路径有很强关联的。考虑这个 dp 过程可以在 lca 拆开 ddp 后合并,但其实完全也可以在任意一点处拆开,然后做点分治:这样我们就直接做到了 \(O(nk\log n)\),再暴力一点,我写的 \(O(nk^2\log n)\) 也是非常快速的。而且我们成功地抛弃了矩阵的存在。事实上很多树上路径题正是因为你可以把他在任何一点处拆成两端,分别研究后合并,于是可以使用点分治,点分的好处是:只要你点分完,那么剩下的问题多半都会变的极其简单和暴力。

记录(离线后建出点分树,把询问挂到点分树上的 lca 一起处理,这样比较方便。)

6. Bracket Cost

考虑一个串怎么做,首先,如果我们要加括号,一定是左括号放最前面,右括号放最后面。

考虑循环右移的本质就是把一个字符往后面放:显然我们只会移动右括号且放到最后。

考虑:前缀和最小的一个地方 \(minn\),和最后的总的前缀和 \(S\),我们将看到:若 \(minn\ge 0\) 答案是 \(S\);否则,若 \(S\ge 0\) 答案是 \(-minn+S\),否则答案是 \(-minn\)

自然地把 \(-minn\)\(S\) 分开来看:我们会发现对于 \(S\),当且仅当 \(S\ge 0\) 的时候会作一个 \(+S\) 的贡献,那么考虑在全局中我们用两个 bit 就可以统计 \(S\) 的贡献。

然后考虑 \(-minn\),首先不考虑 \(minn\le 0\) 这回事:我们会发现如果我们初始设左端点为 \(n\) 然后不断右移,那么可以用单调栈统计答案。

加入 \(minn\le 0\) 的话,考虑对于每个左端点 \(L\),一定存在一个最小的 \(R\) 满足 \(s_L\ge s_R\),那么我们算的就是 \([R,n]\) 这一段的答案,所以用单调栈预处理出每个后缀的答案,然后在对每个 \(L\) 求出对应的 \(R\),查询 \(suf_R\) 即可。

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

记录

7. Majority

套路地我们考虑如何快速判定一个串是否合法。

首先我们考虑这个操作,我们会发现假设我们选中了 \([l,r]\)\(s_{l-1}\)\(1\) 那么 \([l,r]\) 这个操作永远没有 \([l-1,r]\) 优秀,所以我们可以假定任何一个操作 \([l,r]\) 满足 \(s_l=s_r=1\) 的同时都满足 \(s_{l-1}=s_{r+1}=0\)(规定 \(s_0=s_{n+1}=0\))。

那么也就是说我们操作的段永远形如 0(111000111000...000011111)0 这样的。

然后我们会发现,我们可以永远只操作三个连续段构成的串:也就是 111000111 这样的。

此时我们已经可以发现不合法的条件了,就是一个串经过若干次操作以后变成了 1110001110000....000111 这样的串(不妨认为首尾都是 \(1\),因为首尾出现 \(0\) 必然不合法,这部分的计数是容易的。)而且中间任何一段 \(0\),都大于两边的 \(1\) 的个数和。

上面的部分,实际上就是贪心地把能合并的都合并到一起,然后考虑最终的形态的结果,就比较简洁且可做了。

所以我们可以容易想到设 \(f(i,j)\) 是长度为 \(i\) 的串且末尾那一段长度为 \(j\),然后设 \(g(i)\) 是长度为 \(i\) 的合法串个数。

考虑转移,枚举前两段的答案即可做到 \(O(n^4)\),发现是一个二维前缀和的形式,就做到 \(O(n^2)\) 了。

记录

8. MST and Rectangles

奶了一口是 boruvka 结果真的是...

考虑做 logn 轮 boruvka,问题变成了对于每个集合寻找距离这个集合最近的点。

那么首先 \(a_{i,j}+a_{j,i}\) 比较麻烦我们考虑,如果我们对 \((a,c),(b,d)\)\((c,a),(d,b)\) 两个子矩形都做一次 \(+w\) 的操作那么最后 \(i,j\) 的权值就是 \(a_{i,j}\) 了,这样就方便很多。

考虑这个矩形加的形式只有扫描线比较好处理。

我们考虑只用对于每个点 \(u\) 找到和它不在一个集合且连边权值最小的点。

如果没有不在一个集合的限制那么就是 \(a_{u,v}\) 的最小值,这个扫描线的过程中线段树维护一下即可。

如果我们维护一下最小值,和最小值的位置,那么我们可以判断一下最小值是不是合法的,如果不合法,那么我们知道最小值的颜色设为 \(col\),那么我们只需要知道整个 \(a_{u}\) 这个数组里颜色和 \(col\) 不同的位置的最小值即可,显然也是可以线段树维护的,这样单次 boruvka 就是 \(O((n+m)\log n)\) 的。

时间复杂度 \(O((n+m)\log^2 n)\)

记录

8. Telegraph

似乎就是上次 Pikachu 的优化版本,\(n^6\) 人被干麻了。

考虑设 \(dp(i,j,x,y)\) 表示前 \(i\) 层的点已经确定,且排序后前 \(j\) 个数分配完了,且两层各自剩下 \((x,y)\) 个节点。

首先如果 \(x+y\ge n-i\) 那么我们就不用进一步转移了:直接贪心地把下一层和下两层选走。

然后如果第 \(i+1\) 层选了 \(a\) 个那么就会把贡献累加到 \(dp(i+1,j+a,x+y-a,x-a)\) 上。

由此我们可以看出两层的节点数始终不超过 \(n\):所以这个 dp 状态 \(O(n^4)\) 转移 \(O(n)\)\(O(n^5)\) 的。

首先我们注意到层数是不重要的,所以 dp 状态变成了 \(n^3\) 但是转移还是 \(O(n)\) 的。

一般优化转移的时候我们把刷表/填表两种形式都拉出来看一下,也就是有 \(dp(i,j,k)=\min_{0\le x\le i}dp(i-x,k+x,j-k)+suf_{i-x+1}\)

首先我们注意到如果确定 \(i,j,k\)\(j-k\) 是定值设为 \(d\),然后考察我们所取到的值:\(dp(i-1,k+1)+suf_{i},dp(i-2,k+2)+suf_{i-1},dp(i-3,k+3)+suf_{i-3},...\)。我们设 \(g_d(i,j)\) 是定值为 \(d\) 且所取到的最后一项是 \(dp(i,j)+suf_{i+1}\) 的这一串数的最小值,显然 \(g\) 的计算是 \(O(1)\) 的,利用了 \(g\) 的计算就能加速 \(f\) 的计算变为 \(O(1)\)

注意到:当 \(x=0\) 的时候会出现特殊情况,所以我们不妨让上面的 \(x\gt 0\),考察 \(x=0\) 的时候的同层转移:\(dp(i,j,k)\rightarrow dp(i,j+k,j)\),如果 \(k\gt 0\),那么第二维总是严格递增的,如果 \(k=0\),那么就是 \((j,0)\rightarrow (j,j)\),在 \(j=0\) 的时候,第三维是递增的;否则 \((0,0)\) 依旧转移到 \((0,0)\) 没有任何影响。综上我们只需要 rep(j,0,n)rep(k,0,n)... 地去处理这部分就好了。

时间复杂度 \(O(n^3)\),开到 \(750\) 后需要一些卡常,剪去无用状态的手段。

记录

9. Max Intersection Partition

首先观察到交为空的组最多只有一组:如果我们钦定一组为空,那么可以把最大的 \(k-1\) 个全部单独拿出来,剩下的放到这个空组。

接下来我们考虑 \(k\) 组交都不为空:如果线段 \(a\) 包含线段 \(b\)除非线段 \(a\) 单独成段,否则我们会把线段 \(a,b\) 放在一起。

所以我们不放先将 \(a\) 取出来,这样剩下的线段两两都不会有包含关系,我们自然地把他们排序。

然后会发现如果选出来的每一组交都不为空,那么必须有你选的是连续的一段划分进一组。如果我们在这部分选了 \(x\) 组,就会把取出来的那些线段里最大的 \(k-x\) 个拿出来。

这样有一个 \(n^2\) 做法了:设 \(dp(i,j)\) 表示前 \(i\) 个选完选出了 \(j\) 组,单调队列一下 \(n^2\) 就可以通过这个题了。

但是还有更优秀的解法:我们考虑分段那里,一段 \([i,j]\) 的代价是 \(R_i-L_j\)(显然我们可以忽略和 \(0\)\(\max\) 的问题),所以分 \(l\) 段的代价是容易的:找到最优秀的 \(l-1\) 个分段点即可,这样我们把每个 \(i-(i+1)\) 处分段的代价取出来,然后排序,这样就可以 \(O(n\log n)\) 是时间内获得 \(dp(n)\) 的所有值了。

记录

10. Random Walk to Millionaire

我倒是觉得比 Ex 有趣很多。

设最后答案为 \(S\),有 \(E(S)=E(\sum_{i=1}^{k}S_i)=\sum_{i=1}^{k}E(S_i)\),换言之我们分别计算在每一步获得的收益的期望,相加便是答案。

如果我们走到 \(C_v=1\) 的点,收益是确定的 \(1\),那么我们会发现在第 \(i\) 步的期望收益就是在第 \(i\) 步走到 \(C_v=1\) 的点的概率,可以直接设 \(dp(i,j)\) 表示 \(i\) 步后走到点 \(j\) 的概率。

平方的经典手段是考虑组合意义:即 \(X^2=\sum_{i=1}^{L}\sum_{j=1}^{L}[C_i=C_j=0]\)\(L\) 是路径长度。

换言之当你走到 \(C_V=1\) 的一个点的时候,\(X^2\) 相当于你从前面所有 \(C_V=0\) 的点里随便选两个出来的方案数。

\(dp(i,j,0/1/2)\)\(i\) 步走到 \(j\),且选出了 \(0/1/2\) 个点的期望,时间复杂度即为 \(O((n+m)k)\)

注意到组合意义有很强的拓展性:如果我们拓展到 \(X^p\) 累加到答案,则可以在 \(O((n+m)kp^2)\) 的时间内解决:而且注意到此时的转移系数是组合数,我们可以拆成卷积的形式:在 \(O((n+m)kp\log p)\) 的时间复杂度内解决这类问题。

记录

11. Wild West

如果只有两个维度的话,我们考虑按照第一维度降序排序,假设一个人的第一维是在 \(i-(i+1)\) 之间的,那么,设前 \(i\) 个人里第二维度最大的数值是 \(x\),这个人如果想胜利,则他的第二维度必须大于 \(x\),这样可以在 \(O(n\log n)\) 的时间内解决。

现在考虑三个维度,也就是说排序以后仍有两个维度:我们不妨计算此时多少人不合法,也就是说存在某个恶人,剩余两维的数值都大于我们的人员。

如果我们把两个维度放到二维平面上,则相当于我们每次会加入一个矩形(在第一象限),然后每次求所有矩形的面积并。

如果我们考虑最后的图形,会发现它的外边界是一个阶梯状的东西:\(y\) 上的边界随着 \(x\) 的增大而减小。我们用 \(set\) 维护真正有意义的那些边界点,每次加入一个点的时候删掉一些变得无意义的关键点,动态维护整个图形的面积即可:由于每个点插入一次,删除一次,时间复杂度依旧是 \(O(n\log n)\) 的。

记录

12. Choosing Two Paths

感觉是模拟的时候写的非常劣的做法,稍微改改才能得到正解。

约束实质上是要求路径端点不能在路径交上:我们考虑枚举路径交的两个端点,显然只要他们的 \(deg\) 都大于 \(2\) 就一定能作为某两条合法路径的交。

所以引导出一个 \(O(n^2)\) 做法,枚举一个端点并把它定根,然后一路 dfs 下去找一下,然后考虑还有路径长度最大,所以此时对每个 \(u\) 维护一下 \(subtree_u\) 里最深的两个子树。

我们考虑点分治加速这个过程,这个时候会出现枚举重心 \(u\),然后要考虑端点在 \(u\) 的两颗子树内的答案:由于点分的特性导致我们可以像 dsu on tree 处理轻子树那样暴力地合并,于是这个问题在 \(O(n\log n)\) 的时间内得到了解决,常数比较大。

记录

13. Fragile Balls

这个题太牛逼了,不知道 gzy 是怎么模拟场切的,磕头了。

我们注意到除了 \(a_i=b_i\) 的情况外,每个球都会至少移动一次:所以我们来考虑 \(c_i=1\) 的情况。

我们会发现这个题的一个复杂点是我们要保证任何时刻不能有盒子从非空变为空的。

套路的想法是,先找出什么时候会无解,我们观察样例 2:有一个球想从 \(1\)\(2\),有一个球想从 \(2\)\(1\)

自然地,我们发现,如果对于一个初始在 \(a_i\),结束在 \(b_i\) 的球,我们连 \(a_i\rightarrow b_i\) 的有向边,然后,如果出现一个长度大于等于 \(2\) 的简单环,显然是无解的。

因此有非自环的简单环,是无解的充分条件,我们猜测它也是必要的(注意我们的语境是在 \(c_i=1\) 的情况下,因为如果每个点都可以移动很多次,那么在图上会出现连通块相互干涉的情况),理由如下:

由于最终没有点的入度为 \(0\),所以我们考虑对于每个弱联通分量缩点得到的 DAG,然后考虑此时的所有无入度“点”(强连通分量):首先它们都不会是一个单独的点,否则这个点的真实入度就是 \(0\) 了,而且我们一定能在这个 SCC 里找到一个点的 outdeg \(\deg 2\)(就是说初始状态里至少有一个盒子,它的球数大于等于 \(2\),所以你可以把其中的一个球移出去)。

我们找到这个点,设为 \(x\),不难发现至少 \(x\) 有一条出去的边,不可能所有的边都是 \(x\rightarrow x\) 的(唯一的 corner case,就是说,\(x\) 是个孤立点,有若干条 \(x\rightarrow x\) 的边,此时它永远是合法的)。假设有一条 \(x\rightarrow y\) 的边,满足 \(x,y\) 同属一个强连通分量:我们看把一个球从 \(x\) 拿到 \(y\) 这个过程,相当于消去了一条边。如果这个 scc 没有因为断的边,变成多个 scc 的话,说明我们还能重复这个过程;否则,考虑断成了若干 scc,我们此时再拿出“没有入度”的那个 scc,有两种情况:

  • 若这个 scc 依旧不是一个单独的点,那么又再次回到了上面的情况。

  • 若这个 scc 是一个单独的点 \(u\),我们注意到初始的图中 \(u\) 是有入度的,此时却没有了:说明别的最终目标是 \(u\) 的球已经放到了盒子 \(u\) 中,所以此时任何的边 \(u\rightarrow v\) 都是可以直接走的,这样就把 \(u\) 这个点消掉了。

因此,我们说明了:如果每个球移动不超过一次,那么有解的条件就是不存在长度大于等于 \(2\) 的简单环。

现在考虑每个球可以移动多次的情况:我们注意到弱联通分量之间可能会有相互干涉的情况了。这看上去很复杂,其实并没有那么难:关键是我们证明了非简单环的调整都是可以在 \(c=1\) 的情况下完成的,换言之我们先把答案加上 \(\sum_{i=1}^{m}[a_i\neq b_i]\),这样的话所有的非简单环结构都可以看作调整好了。

发现孤立自环也是比较特殊的:因为只有一个球在这个盒子里,且它最后还要待到这个盒子里,如果你想让这个球去帮别人忙,那也必须先有另一个球过来才行,这点和简单环比较像。当然孤立自环除非要帮别人忙,不然没人管它也可以,这是区别所在:而一个“比较大”(不是简单环也不是孤立自环)的弱联通分量里,一个 \(x\rightarrow x\) 的边并不需要别人帮它忙。事实上这些比较大的连通分量里的所有边都不需要别人帮他忙,因为我们已经指出了,这样的连通分量靠自身,且每个球只动不超过一次就可以完成目标,那么对于动的球(即 \(a\neq b\))我们让每个球在即将“动一次”的时候,先从 \(a\) 挪到外面的连通块里,然后等用完它了,最后一次移动把他放到 \(b\) 处,这样它产生 \(c-1\) 的额外次数;对于本来不动的球(即 \(a=a\)),由于它不是孤立自环点,所以总有一个时刻 \(a\) 这个盒子的球数是大于等于 \(2\) 的,我们就在这个时刻把 \(a\) 拿出去做贡献,做完了以后把它塞回来,这样它产生 \(c-2\) 的额外次数啊。

经过上面的讨论最后可以归纳出几类:

  • 长度大于等于 \(2\) 的简单环,它必须有一个点给他做 \(1\) 的贡献,当做完贡献以后,环上的每条边可以花费 \(1\) 的额外步数(就是 \(\sum_{i=1}^{m}[a_i\neq b_i]\) 以外的)造成 \(c-1\) 的贡献。

  • 孤立自环,它可以不使用;如果使用,也必须有一个点给他做 \(1\) 的贡献,它自己花费 \(2\) 的额外步数造成 \(c-1\) 的贡献。

  • 大的弱联通分量上的非自环边:使用没有前提条件,花费 \(1\) 的额外步数造成 \(c-1\) 的贡献。

  • 大的弱联通分量上的自环边:使用没有前提条件,花费 \(2\) 的额外步数造成 \(c-1\) 的贡献。

如果对四类编号为 \(1,2,3,4\),我们发现是 \((3,4)->(2)->1\) 这样一个层级。

如果我们对于每个 \(1\) 类的环,设有 \(X\) 个,直接把答案加上 \(X\) 先,不难发现此时 \(1\) 类边不花费额外步数,\(2\) 类自环造成的贡献变为 \(c-2\)\(3\) 类边不花费额外步数,\(4\) 类边花费 \(1\) 的的额外步数变成 \(1\)

最后我们的目标是从四类元素里选出额外步数最少的若干个元素,且他们的贡献大于等于 \(X\),容易在 \(O(m\log m)\) 的时间内解决。

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

记录

14. Safe Travel

考虑建出最短路树,则对 \(u\) 求答案的过程相当于 ban 掉最短路树上 \(fa\rightarrow u\) 的边,求此时的 \(1\rightarrow u\) 的最短路。

我们称 \(u\) 子树为 \(T\),其余部分为 \(S\)

首先,新的最短路至少有一条边是跨越 \(S-T\) 的,这容易理解;然后,我们发现跨越 \(S-T\) 的边应该恰好只有一条:否则 \(S\rightarrow T\rightarrow S\) 的话就违反了最短路的定义。

假设我们走了一条跨越 \(S-T\) 的边 \((u,v)\)\(u\in S,v\in T\)。我们会发现 \(1\rightarrow u\) 的最短路是 \(depth_u\)\(v\rightarrow i\) 的最短路是 \(depth_v-depth_i\)

这样对于每条非树边的代价就是 \(depth_u+depth_v+w\),然后树上覆盖一下就随便做了。

可以做到 \(O(n\log n)\),对非树边排序后用并查集。

记录

15. Cellular Automaton

一个很厉害的题。

首先这个生成方式长得就很迷惑,而且加上了一个看上去没法做的判定:任何输入状态,执行过程中 \(1\) 的数量不变。

生成方式就是说 \([-w,w]\) 这个区间内的上一层的内容决定了下一层这个位置的内容,把那个数组看作一个 \(2w+1\) 的串到 \(0/1\) 的映射,这样就好理解一些了。

我们来考虑给定一个映射的话怎么判断是否合法。

我们会发现一个事情:就是设 \(I\) 是全 \(0\) 串则 \(f(I)\) 必然是 \(0\),否则初始所有位置都是 \(0\),然后你下一轮就会变成全是 \(1\)

类似地,如果 \(I'\) 是全 \(1\) 串则 \(f(I')\) 必然是 \(1\)。这两个是比较特殊的。

否则,我们会发现大多数情况下你的状态都是从无穷个 \(0\) 开始,无穷个 \(0\) 结束。

我们用一个类似滑动窗口的东西从左到右扫描这 \([-w,w]\) 个区间,那么会在串与串之间连边。

首先这个图显然是强连通的,然后合法的概念就是任何一条 \(I\rightarrow I\) 的回路,我们设 \(x\) 是拓展出 \(1\) 的个数而 \(y\)\(f=1\) 的结点数则有 \(x=y\),这个还是不太好做,我们考虑把 \(u\rightarrow v_{i}(i\in \{0,1\})\) 的边权改成 \(f(u)-i\) 这样就相当于任何一条 \(I\rightarrow I\) 的回路都是 \(0\)

我们很容易地看到:上述条件合法当且仅当对于每条边 \((u,v)\)\(dis_u+w(u,v)=dis_v\)\(dis_I=0\)

证明大概还是画出最短路树,然后树边根据定义就是满足这个的,返祖边如果不满足这个那么说明有正/负环,你可以绕很多圈然后再利用强连通的概念跑回去;横叉边的话是类似的?

后面的差分约束判定有无解就是常规的了。设 \(N=2^{2w+1}\) 则时间复杂度上界为 \(O(N^3)\)

记录

16. Evacuation Pla

题意是给出一个网络流图的一个时刻的状态,保证这个时刻是图的最大流,然后问它是否是最小费用流,如果不是,构造一组更优的解(不需要是最优解)。

显然我们可以直接跑一边最小费用流来比对答案,但这样的话复杂度就有点过高了。

然后引出了一个消圈定理:也就是求最小费用最大流的另一种方式。我们只需要先跑出一次最大流,然后不断在残量网络上寻找负权圈并增广,当无圈可消的时候就得到了原图的最小费用最大流。

所以我们就相当于在这个图上找一个负权圈,如果有的话再对其增广。

寻找具体负环的方式也很简单:我们跑 bellman-ford \(n\) 次以后如果还有一个点 \(u\) 可以被增广,说明 \(u\) 此时所在的最短路径点数 \(\gt n\) 所以这条路径上一定有一个环,这样这个题就在 \(O(NM)\) 的时间内解决了(\(N\) 是点数 \(M\) 是边数)。

记录

17. Picking Strings

首先 \(B,C\) 等价。

操作:\(A\rightarrow BB,B\rightarrow AB\),删除 \(AAA\)

所以 \(B\) 的个数永远不会变少,且差值 \(d\) 是偶数。判掉这两种情况。

同时注意到,如果一个 \(A\) 的后面(不要求相邻)有 \(B\) 则我们一定可以删除它。

关键想法:生成数目正确的 \(B\) 然后删除此时的所有 \(A\),重新生成数量正确的 \(A\) 即可。

假设 \(T\) 的结尾不是 \(A\)

  • \(S\) 的结尾不是 \(A\),则我们的想法可行。
  • \(S\) 的结尾是 \(A\)
    • 假设 \(S\)\(B\) 的数目和 \(T\)\(B\) 的数目已经相等,则查看结尾 \(A\) 的数量是否是 \(3\) 的倍数:如果不是,则必定无解;如果是,则我们只能全部删除这些 \(A\),然后成为了一个子问题。
    • 假设 \(S\)\(B\) 的数目和 \(T\)\(B\) 的数目不相等,则我们在结尾生成两个 \(B\) ,此时回到上面的情况。

假设 \(T\) 的结尾是 \(A\)

  • \(S\) 的结尾不是 \(A\),则显然无解。
  • \(S\) 的结尾是 \(A\)
    • 假设 \(S\) 结尾处 \(A\) 的数量比较少,依旧无解。
    • 假设 \(S\) 结尾处 \(A\) 的数量和 \(T\) 相等,假设 \(S\) 全部由 \(A\) 组成,则我们还要判断 \(T\) 是否全部由 \(A\) 组成;否则,变成 \(S,T\) 结尾都不是 \(A\) 的情况,一定有解。
    • 假设 \(S\) 结尾处的 \(A\) 的数量比 \(T\) 多,设多 \(k\) 个,首先当 \(S\)\(B\) 的数目比 \(T\) 少的时候一定有解,我们考虑 \(S_B=T_B\) 的情况:若 \(3\nmid k\) 则无解,否则变回了上面的 case。

看上去很多?但是模拟的时候想的还是挺顺的。

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

记录

18. SolveMe

很神仙的数数题。

首先我们会发现 \(A,B\) 都是一个基环树的形态,然后会发现基环树就不太好研究,我们大胆猜测 \(A,B\) 都是排列。

我们会发现:\(X\neq 0\) 的时候 \(A\) 一定不是排列,否则,必定有两个点,他们开始沿着 \(A\)\(X\) 步会走到一个相同的点,而你又要保证从这个点开始继续走能走回到两个不同的点,这是矛盾的。推出 \(A\) 是排列后,用类似的手法说明 \(B\) 一定是排列。

考虑 \(X=0\) 的情况:我们发现 \(Y\neq 0\) 或者 \(Z\neq 0\) 的时候可以类似地说明 \(A,B\) 一定是排列。

而当 \(X+Y+Z=0\) 的时候这个问题是容易的。所以以下我们考虑 \(X+Y+Z\gt 0\) 的时候,此时 \(A,B\) 一定是排列。

那么也就有 \(A^XBA^YBA^Z=I\),则有 \(BA^YBA^Z=A^{-X}\),也就有 \((BA^Y)^2\times A^{Z-Y}=A^{-X}\),令 \(C=BA^Y\) 则有:\(C^2=A^{Y-(X+Z)}\)

\(x\) 替换 \(Y-(X+Z)\),由于 \((A,B)\)\((A,C)\) 之间是一一对应的,所以我们就是对合法的 \((A,C)\) 计数,满足:\(A^{x}=C^2\)

考虑如果知道了 \(A\) 那么 \(A^x\) 的形态,我们用置换环来描述排列:一个长度为 \(L\) 的环会被拆成 \(\gcd(L,x)\) 个长度为 \(\frac{L}{\gcd(L,x)}\) 的环。所以我们考虑倒着还原回去:也就是说,如果我们确定在最后共有 \(k\) 个长度为 \(x\) 的置换环(且他们的形态已经确定),他们存在于 \(A^{x}=C^2\),我们分别算出,这 \(k\times x\) 个元素在 \(A\) 中的形态方案数,以及在 \(C\) 中的形态方案数。

容易发现 \(C^2\)\(A^x\) 的一个特例,所以我们只考虑后者的计算。

我们预处理 \(f(i,j)\) 表示 \(A^x\)\(j\) 个长度为 \(i\) 的环,合并回 \(A\) 的方案数。

枚举 \(i\) 以后,我们枚举在 \(A\) 中原本的环长 \(L\),这样可以得到一个长度为 \(L\) 的环拆出 \(g\) 个长度为 \(i\) 的环(需要注意所有合法的 \((i,len)\) 共有 \(O(n)\) 个,因为你确定了 \(len\) 就确定了 \(\gcd(len,x)\) 就确定了 \(i\)),然后我们直接做背包即可。

这看上去需要四重循环:\(i,L\) 是前两重,背包有一重维度 \(k\) 表示更新 \(f(i,k)\),还有一个 \(cnt\) 表示我们拆掉了 \(cnt\) 个长度为 \(i\) 的环。

首先前两个维度是 \(O(n)\) 的,\(k\) 的上界是 \(n/i\)\(cnt\) 的上界是 \(k/g\) 的,我们算一下会发现不是很大,大概是 \(n^2 \ln n\) 级别的。

预处理 \(f\) 过后考虑设 \(dp(i,j)\) 表示确定了置换环长度小于等于 \(i\) 的置换环,共涉及 \(j\) 个元素,转移枚举长度 \(i+1\) 的环的使用个数,这个也是 \(n^2 \ln n\) 的。

时间复杂度 \(O(n^2\ln n)\)

记录

19. 01? Queries

就当作 ddp 的复习题了。

首先考虑单个问题怎么做,不太会,考虑如果没有问号怎么做。

这是个很套路的dp:我们知道一个子序列可能会出现多次,考查所有的出现位置,我们把一个子序列挂在所有出现位置中,结尾最靠前的那个位置。

就是说 \(00100\),你会把 \(00\) 归到属于位置 \(2\) 的子序列,把 \(010\) 归到属于 \(4\) 的子序列。

那么设 \(dp(i)\) 是归到 \(i\) 的子序列个数,设 \(s_i\) 上一次出现的位置是 \(j\),则 \(dp(i)=\sum_{k=j}^{i-1}dp(j)\),初始 \(dp(0)=0\)

现在加入问号,首先如果 \(s_i\) 不是问号那么 \(s_j\) 可以等于 \(s_i\) 也可以等于问号;如果 \(s_i\) 是问号我们把它当作 \(0\) 做一次,当作 \(1\) 再做一次,求和就是 \(dp(i)\)

所求即为 \(\sum_{i=1}^{n}dp(i)\)。到这里我们学会了一个 \(O(nq)\) 的做法。

考虑单点修改后再次询问这个东西,由于单个问题是 dp 所以我们考虑 ddp。

首先我们需要把 \(\sum_{k=j}^{i-1}dp(j)\) 变成前缀和的形式。

然后对于位置 \(i\),我们记录 \(S_i,S0_{i},S1_{i}\)\(S0_{i}\) 的定义是 \(i\)(含)之前第一个为 \(0\) 或者问号的位置,设为 \(j\)\(S_{j-1}\) 的值。

这个东西是可以用矩阵转移的。时间复杂度 \(O((n+q)\log n)\),有矩阵乘法的常数 \(3^3\)

记录

20. 喵了个喵

构造好题,但我希望别正赛出,没场切心服口服。

考虑 \(k=2n-2\):我们留出一个空栈,然后剩下 \(n-1\) 个栈最多放两个不同元素,此时若加入一个元素,它要么在上层存在,要么在下层存在。

考虑 \(k=2n-1\):我们不妨假设 \(n-1\) 个栈都填满了两个元素,加入了最后一种元素 \(x\),若直到 \(x\) 的下一次出现为止,都没有新的底层元素出现过,则把 \(x\) 直接放进空栈即可。

否则我们设出现的底层元素是 \(y\),所属的栈为 \(S\),栈 \(S\) 的顶层元素是 \(z\)

\(z\)\(x\rightarrow y\) 的过程中没有出现,我们把 \(x\) 放到 \(S\) 中即可。

否则若出现了奇数次,我们把 \(x\) 放到空栈中即可,直到遇见 \(y\),否则所有的 \(z\) 都放到栈 \(S\) 中。

否则若出现了偶数次(且大于零),一样把 \(x\) 放到空栈中,首先把一个 \(z\) 放到 \(x\) 的那个栈,剩下的奇数个 \(z\) 都放到栈 \(S\) 中。

时间复杂度 \(O(\sum m\log n)\)

代码到时候发。

21. Median Replace

考虑怎么判定一个确定的串是否合法。

题目的本质是消去所有 \(0\)

本质就是 \(000\) 可以变成 \(0\),而 \(01/10\) 可以直接消

考虑每个极长段,不妨认为 \(0\) 的数目是 \(1/2\)

结论是如果有 \(000\) 那么我们先消,否则我们随便选择一个 \(01/10\) 的形式消除,除非只剩下若干个 \(0\),否则就能不断消除。

注意到过程 \(2\) 的过程中是会产生新的 \(000\) 的。

我们可以维护一个栈。

加入一个 \(1\) 的时候:若栈中是 \(1\),我们直接加入;若栈中是 \(0\),根据结论把栈顶弹出且不加入。

加入一个 \(0\) 的时候,若栈中是 \(0\),我们加入并消去 \(000\) 形态;若栈中是 \(1\),我们直接加入

为什么最后一种情况碰到 \(\text{10}\) 这种形态的不消?

大部分情况下只用消 \(\text{01}\) 这种形式就够了,除非到最后的段明确是 \(\text{111..000}\)

所以最后的栈应该长成 \(\text{111...000}\) 且如果合法最后 \(1\) 的个数要大于等于 \(0\)(其实是大于,但是不可能两个数相同的,这样就违反了串长奇数)

我们可以发现任何时刻栈的 \(0\) 的个数是 \(\le 2\) 的,所以 \(1\) 的个数也只需要 \(\le 2\) 即可。

\(dp(i,x,y)\)\(1\sim i\) 且栈的形态是 \(x\)\(1\)\(y\)\(0\) 即可。

复杂度 \(O(n)\) 常数为 \(9\),也许可以出成 ddp 的。

记录

posted on 2022-11-02 10:31  Cry_For_theMoon  阅读(301)  评论(8编辑  收藏  举报