Cry_For_theMoon  

1. 填数游戏

你是第一个.jpg

考虑性质 B,我们会发现无论 Alice 怎么选,Bob 只有两种选择:\(1,2,...,n-1,n\) 或者 \(2,3,...,n,1\)

考虑 \(S_i\)\(T_i\) 的交集,如果交集为空,则我们可以忽略;如果交集恰好为 \(1\),Alice 一定会去选这个交的,否则交集为 \(2\)

设有 \(a\) 个交集为 \(1\) 的是交了 \(i\)\(b\) 个交集为 \(1\) 的是交了 \(i+1\),有 \(c\) 个交集为 \(2\)

Alice 的任务其实是把 \(c\) 分配给 \(a,b\) 加上去,然后 Bob 选择其较小值。

我们此时容易 \(O(1)\) 计算出最优秀的结果。

考虑更一般的情况,我们希望沿用性质 \(B\)。首先,可以对 Bob 的选择集合做出一下的转化:

如果一个集合大小为 \(1\),则我们确定它就是选这个数 \(x\),则考虑 \(x\) 在其它集合中的出现,该集合只能选另外一个,递归做这个过程。

总之我们可以在 \(O(n)\) 的时间内消去这类只有一种选择的,现在每个位置都有两种选择。

把两个选择连边,则如果有一个环,我们可以暴力消去它,否则一定是若干个森林(由于无环)。

消环看上去是一个很困难的事情,但其实冷静思考并不是:一个连通块要么是树要么是基环树,不然就无解了。

基环树的话,环就直接套性质 B,然后环上的点等价于已经被占用了,因此剩下的部分也是方案唯一的,套用上面的化简过程。

树则比较麻烦。

最直接的想法是:我们对每条边,选深度比较大的那个作为真正的点,这样是合法的。

当然树根也是可以被占用的,这样相当于选一个点 \(u\),它开始向上的边全部往上推,这样就把选中的点空了出来(显然我们恰好空一个点)。

这是 Bob 的方案,考虑 Alice。

先来考虑限制 C:如果 Alice 拿到的数和 Bob 没有交那就无所谓,否则如果这个点是比较深的点,则相当于当且仅当 Bob 选择 \(u\) 子树外的点会匹配上;如果是浅的点,等价于 Bob 选择 \(u\) 子树内的点才会匹配上。

所以我们执行子树加 / 子树外+,在 dfn 序上差分做这个事情,最后序列的最小值就是这棵树的答案。

场上就想到这里,但是没有写完。

一般情况的话,Alice 相当于是多了一种情形:可以自己选择子树 + 或者子树外 +,然后让最小值最大。

看上去套个二分先,但其实发现没有什么更好的性质了。

如果两个点不是祖孙关系,则不会同时执行子树加,这个没有同时执行子树外加优秀。

所以执行子树加的是一条根链。

初始的时候,把所有人都设置成子树外加的形态,然后在树上 dfs,把当前点的子树外加改成子树加,这是 Alice 的一种方案,此时的最小值是 Bob 对应的结果。

因此我们要支持:区间加减,全局求 min,上线段树维护就好了,复杂度 \(O((n+m)\log)\)

2. 弹跳

如果是一维的情况,可以发现其就是线段树优化建图后求最短路的模板,所以我们依旧考虑优化建图。

二维情况下线段树不太好维护,考虑上 kd-tree。

由于 kd-tree 和 seg 的结构有区别,我们需要把每个点拆成 in 和 out 两个点:in 之间按照 kd-tree的结构连边,而 in 连向自己的 out,每个弹跳装置是 out 向对应的 in 连边。

根据 2-d tree 上查询矩形的结论,边数最多是 \(O(m\sqrt n)\) 的,所以最坏情况下它的复杂度是 \(O(m\sqrt n\log)\)

\(O(m\sqrt n)\) 的空间开销很大,所以需要隐式建图:注意为了方便,in的连边可以显式连出来,而 out 向 in 这部分直接在 kd-tree 上搜索即可。

这样的话空间可以接受,但是时间仍有点慢,一个剪枝是在 kd-tree 上搜索的时候如果当前 in 点的 dis 不可能被松弛了(\(dis_u + w \ge dis_v\))则显然其子树内都不会被松弛,直接退掉即可。

记录

3. Unite

考虑按行 dp,设 \(f(i,S)\) 是考虑完第 \(i\) 行,第 \(i\) 行的所有格子连通性为 \(S\)

由于我们还要额外记录这个格子是否是黑色,所以其实状态数是 \(Bell(m+1)\) 的。

转移的时候直接 \(2^m\) 枚举然后重新计算即可。

注意到我们不需要每一行的转移都去重新计算:可以预先算出 \(S\) 加一个行的状态后会是什么样子的,这样转移可以做到 \(O(1)\)

时间复杂度 \(O(n2^mBell(m+1))\),常数很小。

记录

4. 最小度限制生成树

好题啊好题。

如果上来就套 wqs 二分其实是很无趣的做法。

考虑题目可以变成这样一个形式:每个点(除了 \(s\))有一个点权 \(w\),实际意义是和 \(s\) 的所有连边里的最小边权。

我们相当于选出 \(k\) 个点,然后删去一些边形成 \(k\) 个森林,保证一个森林恰有一个选出的点,而答案是 \(k\) 个点的边权和再加上剩下的边权和。

考虑先求出剩下 \(n-1\) 个点的 MST(可能是森林),显然最后只会删去其上的边。

注意断开后,每个连通块实质上的过程是独立的,我们只需要归并地把新增的贡献合并(这里也利用到了凸性),这个思想也可以处理多颗树的情况。(事实上很多有凸性的题都等价说你可以在原有方案的基础上拓展)

我们考虑在选出 \(i\) 个点的基础上拓展到 \(i+1\) 个点,则新加的点必定和某个已经有的点在一个连通块,断开的一定是路径上最大的边。

考虑第一次选点肯定是选在 \(w\) 最小的,以它作为根,那么首先若干颗子树是独立的,考虑 MST 过程中断的最后一条边,这条边的子树内,只要选择了其中的一个点,则必定是选择其中 \(w\) 最小的点,且断掉我们所考虑的这条边。

这个过程和 Kruskal 的过程非常相似:我们只需要在过程中维护每个连通块里 \(w\) 最小的点即可。

最后把贡献全部排序,取前若干项即可得到答案。时间复杂度 \(O((n+m)\log n)\),常数非常小,瓶颈在于排序。

记录

5. Mowing Mischief

考虑这个题是要在走过的关键点最多的基础上去最小化总面积,因此我们可以求出 \(f,g\) 分别表示总个数和总面积。

\(f\) 的计算较为容易:我们按照 \(x\) 这一维度排序后只需要 bit 维护即可。

考虑计算 \(g\),首先我们从 \(f=a-1\) 的转移到 \(f=a\) 的所有点。

首先可以看出:同层的这些点,随着 \(x\) 的增加,\(y\) 不断下降。

现在我们考虑 \(j,k\) 都能转移到 \(i\),在忽略 \(x_j,x_k\le x_i\) 以及 \(y_j,y_k\le x_i\) 的情况下,不妨设 \(x_j \lt x_k\),我们来考察什么情况下 \(j\) 的转移比 \(k\) 优秀。

也就是 \(g(j) + (x_i-x_j)(y_i-y_j) \le g(k)+(x_i-x_k)(y_i-y_k)\)

这看上去类似斜率优化,但牵扯到了两个变量,所以是不可套用的。

我们拆一下式子,左边都是和 \(i\) 有关的项,右边是无关的,可以得到 \(x_i(y_k-y_j) + y_i(x_k-x_j) \lt C\) 的一个形式。

其中 \(y_k-y_j\)\(x_k-x_j\) 是常数,因此可以看成 \(Ax+By\lt C\) 的形式。

注意到 \(A\) 为负 \(B\) 为正,所以其对应的一个半平面是斜率为正的半平面,且位于半平面的下半部分。

这告诉我们:如果对于一个 \(i\) 来说,\(j\)\(k\) 更优秀,那么对于更大的 \(i'\) 来说,也有 \(j\)\(k\) 优秀。

这就牵扯到了决策单调性:不过是反着来的,点越靠后,决策点反而越靠前。

现在考虑加入合法性的限制,其实也很简单:注意到可选的决策点构成一段区间,我们把每个点插入决策点构成的线段树上的 \(O(\log)\) 个区间上即可,然后套用决策单调性。

时间复杂度 \(O(n\log^2 n)\),但实测下来非常快,在 USACO 的官方机器上也能 0.5s。

记录

6. Coprime Permutation

好神的题啊。

打个表来算没有限制的情况,发现其实我们只关注质因子集合而不关注每个质因子的具体出现次数。

但这还是不够的,注意到打表的时候 \(n=5\) 可以换 \(3,5\),还有就是 \(1\) 可以和某些质数互换。

第二个现象比较容易理解:就是 \(1\) 可以和大于 \(n/2\) 的质数互换,因为这些质数是没有倍数在的,它们和 \(1\) 都满足:和其余任何数都互质。

第一个现象比较神秘,最开始和 45dino 猜的是大于根号级别的质数可以互换,但是研究更大一点的表的时候发现是错的。

比较合理的解释是我们把所有的 \(kp_1\)\(kp_2\) 对应交换了,这样就是对的。

那也就是说 \(\left\lfloor \frac{n}{p_1} \right\rfloor = \left\lfloor \frac{n}{p_2} \right\rfloor\),这样就是可以互换的。(注意我们认为 \(1\) 也是质数且其被 \(n\) 除是 \(1\),这样就和大于 \(n/2\) 的质数互换对应上了)。

这样就可以做没有限制的情况了。

考虑有限制的话,考察 \(i\)\(a_i\) 的质因子集合,由于若 \(p_1,p_2\) 不同但被 \(n\) 除后下取整的结果相同,所以它们一定大于等于根号,也就是只会是一个数的最大质因子。

所以若 \(i,a_i\) 去掉最大质因子后的质因子集合仍然不相同,那么无解。否则两质因子被 \(n\) 除的下取整结果应当相同,分别设为 \(x,y\),则我们相当于构建了这样一个映射:

所有最大质因子为 \(x\) 的地方都替换成为 \(y\) 的,所有最大质因子为 \(y\) 的数都放到了最大质因子为 \(x\) 的地方。

这样如果出现了矛盾的映射也是无解。

时间复杂度 \(O(n\log n)\),瓶颈在分解质因数。

记录(CF炸了只能挂这个了)

7. Go around a Circle

感觉自己能独立做 AGC E 还是很难得的,流泪。

不妨认为第一个字符是 R。

注意到不可能有两个 B 连续出现,不然取它们之间的这个位置,第一步往哪里走都是 B。

因此我们可以把整个结构视为:若干个 B 把环分成了若干个 R 的连续段。

会不会没有 B?这个 corner case 只在全是 R 的时候可能出现,且依旧满足没有连续的 B,讨论第一个人是不是 B,然后算一下 fib 即可。

否则我们考虑出现了至少一个 B,把连续的 B 合并在一起,那么形如 R...R B R...R B ... 的形式(注意最后一个 B 后面可能是空的,这是一个细节)。

考虑每一段 R 的长度。

第一段 R 的长度 \(x\) 实质上决定了每个连续段的长度上限,如果你这个段的长度大于 \(x+1\),则我们会发现无论如何是不能走出去的(注意奇偶性还要正确,如果你走出去但还剩下奇数步也是无效的)。

同时可以发现无论如何不可能有偶数段,因为其同时存在两个位置:一个距离两边都是奇数,一个距离两边都是偶数,必然有一个不合法。

事实上,当我们走完 S 的一个连续段的时候,我们必须位于环上某个连续段的最左边或者最右边,而接下来的 B 的奇偶性决定了你是依旧在这个段还是在下一段,但是这是不重要的:因为我们可以发现无论如何,由于方式都是统一的,所以任何时刻每个段的最左边/最右边都有一个人,也就是不存在哪一段不被约束(感性来说其很好理解,因为所有段的地位是等价的)。

因此考虑,如果走到了一个偶数的步数,实际上我们可以来回折返,这是很自由的;但奇数步的话,唯一的方式就是横穿整个段。

换言之考虑第 \(2\) 段开始的所有奇数元素,不能有一段大于这些奇数元素的其中一个。

因此转化成了这样一个问题:用偶数元素凑出 \(n\)(考虑每个段后面附带的一个 B)的方案数,且钦定了它的上界。

插板插疯了就会寄,其实非常简单,这个就是前缀和优化一下 dp,相当于从 \(0\) 走到 \(n\) 钦定每步的上限罢了。

因此整个问题在 \(O(n+m)\) 的时间内得到解决,一个 corner case 是,如果最后一段 R 的后面没有 B,则其实它是不是奇数都不会造成限制(因为是允许不走完它的)。

记录

8. strings

神秘题。

考虑从 \(n\le 30\) 入手去折半。

枚举左半边的构成 \(L\),然后其匹配若干个串的前一半,设这个集合为 \(S\)

\(f(i)\) 表示第 \(i\) 个串的右半部分,和哪些右半部分的构成是匹配的(这是一个 \(2^{15}\) 的数组),那么把 \(S\) 中的所有 f 或起来,其 popcount 就是答案。

考虑上述过程容易想到 bitset 优化,这样复杂度是 \(O(\frac{2^nq}{\omega})\) 的(至少比 \(2^nq\) 好,乐)。

发现预处理仅为 \(2^{n/2}\times nq\),相比于后面的部分来说,计算量太小,因此思考平衡一下复杂度。

可以把 \(q\) 个串分块,设块长为 \(B\),对于每一块的 \(2^B\) 个子集都去处理一下 or 的结果。

这样的话最后每次就只需要把 \(\frac{q}{B}\) 个数组而不是 \(q\) 个数组 or 起来,就平衡了。

那么前半部分是 \(O(\frac{q2^{B+15}}{B\omega})\) 的,后半部分是 \(O(\frac{2^{30}q}{B\omega})\) 的,取 \(B=12\) 即可通过。(感觉理论上最优秀的是 \(B=15\),但是会 MLE)。

(虽然我觉得,这个算量依旧很大啊)

记录

9. Approximate Diameter

考虑如果我们能在 \(T\) 的时间内算出一张图的直径,则根据这个输出方式,我们的复杂度可以做到 \(T\log n\log p\)

因为答案首先单调下降,然后我们只需要找到第一个位置满足 \(2f' \lt f\) ,前面的位置都可以直接输出 \(f\) 作为答案。

但是图直径是不能 \(O(n+m)\) 之类算的。

考虑此时其实只利用了 \(\le 2f\) 的性质,没有利用到 \(\ge \left\lfloor \frac{f}{2} \right\rfloor\) 的性质,因此猜测我们并不需要求直径,只需要求一个在该范围内的值即可,不难发现依旧可以不断找到第一个 \(2f' \lt f\) 的位置,之前的位置依旧可以利用 \(f\) 作为答案。

结论:任意选一个点 \(u\),考虑离他最远的点到他的距离 \(s\),则有 \(s\ge \left\lfloor \frac{f}{2} \right\rfloor\)

我们设直径位于点对 \((x,y)\) 间,考虑 \(dis(x,u) + dis(y,u) \ge f\) 因此两者必有一个 \(\ge \left\lfloor \frac{f}{2} \right\rfloor\),证毕。

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

记录

10. Good Division

很少能自己想出 ARC F,虽然比 \(O(n)\) 的官方做法多了两个 \(\log\),很有感觉。

考虑一个段合法的充要条件,根据 1699D 的结论,也就是长度为偶数且不存在绝对众数。必要性显然,充分性考虑,我们总能找到一对,其中一个是众数 \(a\) 的,直接删去这一对,则依旧不存在绝对众数,用这样的归纳手段可以说明充分性。

现在考虑 dp:那么也就是 \(dp(i)=\sum dp(j)\times f(j,i)\)\(f(j,i)\)\(1\) 表示 \((j,i]\) 为偶数且不存在绝对众数。

不妨容斥掉:也就是我们把所有偶数长度的区间先算出来,再去掉那些有绝对众数的位置。

这里需要观察出一个性质:对于一个序列,设 \(pre_i\) 是其 \(i\) 这个前缀的绝对众数,则不同的取值只有 \(O(\log)\) 种。

因为我们考虑让不同种类数取到最大的构造一定是:1 22 3333....,也就是每一次都比前面的总和恰好多一,那么也就是 \(f_i=2f_{i-1}+1\) 以及 \(f_1=1\) 得到 \(f_n=2^n-1\)

现在假设我们能找到这 \(O(\log)\) 个特殊值,我们会发现计算的时候,它们之间是独立的,因为一个区间最多只有一个绝对众数。

但是如果给你一个 \(v\)\(i\),怎么求所有 \(dp_j\) 的和,满足 \((j,i]\) 的绝对众数是 \(v\) 呢。

\(c_i\)\(\le i\) 的数里出现了多少个 \(v\),则有 \(2(c_i-c_j) \gt i-j\);令 \(f_i=2c_i-i\) 也就是 \(f_i\gt f_j\)

因此我们只用把 \(dp_j\) 打在 \(f_j\) 的位置,然后求 \(\le f_i\) 的所有位置的和就可以了,用 BIT 可以解决。

但看上去我们要对 \(n\) 个值都维护对应的 BIT,虽然我们查找的时候每次只要查 \(O(\log)\) 个 BIT 的信息。

我们来反向考虑:对于每个 \(i\),求出以 \(i\) 为左端点,多少个值可能成为某个 \([i,j]\) 的绝对众数。

我们知道最多只有 \(O(\log)\) 个值,所以我们只需要把 \(dp_{i-1}\) 插进这 \(O(\log)\) 个值的 BIT 里就行了。

这样我们就只用做 \(O(n\log n)\) 次修改,在 BIT 上所以是 \(O(n\log^2 n)\) 的,需要对 \(f\) 离散化。

现在考虑怎么快速找到这 \(O(\log)\) 个数。

以第一部分找数为例,容易发现如果一个数 \(v\) 可能作为 \((j,i]\) 的绝对众数则 \(a_{j+1}\) 一定是 \(v\)

我们其实相当于要找到一个位置 \(j\) 使得 \(f_i-f_j\) 最大,设 \(pre_x\)\(x\) 这个数而言 \(f\) 的前缀最小值,则我们在扫到 \(a_i\) 的时候只更新 \(pre_{a_i}\)(用 \(f_{i-1}\))就是对的。

但考虑每次加数后,所有的值的 \(f_i\) 都会变的。

设上一次 \(v\) 出现的位置是 \(x\),则 \(f_i=f_x - (i-x)\),所以我们其实维护的是 \(f_x + x-pre\),因为 \(i\) 在询问的时候是常数,把所有值按照这个排序,每次暴力检查最大的若干个直到小于等于 \(i\) 的出现了就 break 掉,用重载运算符的 set 维护,则找到所有数的复杂度是 \(O(\log^2)\) 的。

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

记录

11. Li Hua and Path

我们把一条路径投射到 \(\max\) 上,这样第 \(i\) 次询问相当于是求被投射到了 \(\le n+i\) 的位置的路径的总数,做个前缀和就好。

这个约束很容易想到容斥,也就是分成三部分:\(\max\) 是端点,\(\min\) 是端点,同时是端点的。

第一部分很好做,我们直接从小到大加入点,加入点 \(i\) 的时候其连通块大小 \(-1\) 也就是答案(因为不能到自己)。

第二部分比较麻烦,如果从大到小加入的点,则我们确定的是 \(\min\),但我们要把答案投射到 \(\max\) 上,也就是给连通块内的其它点都加一。

这个操作可以在并查集上打 tag,当合并的时候采用启发式合并,把 size 比较小的那一边的 tag 暴力释放掉,复杂度是 \(O(n\log n)\) 的(认为 \(n,m\) 同阶)。

第三部分的想法是点分,这个做法很无趣,常数也不小(\(n\log^2 n\)),其实有更好的做法:

考虑路径最大最小值可以建出 Kruskal 重构树,本题是点权,第一个想法是把一条边换成 \(\max\{u,v\}\),这样建出来符合条件,但是要满足的东西有点多:\((u,v)\) 在 max-tree 上的 LCA 的权值是 \(u\),在 min-tree 上的 LCA 的权值是 \(v\),这个看着是很难维护的。

实际上我们可以直接建出点权形式的重构树,其不再是二叉树:以构建 max-tree 为例,我们只需要从小往大加入树,当加入 \(i\) 的时候,考虑所有 \(j\lt i\) 的邻居的连通块都接到 \(i\) 上并且以 \(i\) 为根就好了。这样的话两点路径的 max/min 依旧是对应树上的 LCA,但是我们把条件转成了:\(u\) 在 max-tree 上是 \(v\) 的祖先且 \(v\) 在 min-tree 上是 \(u\) 的祖先。

我们在 min-tree 上 dfs,当遍历到一个点的时候,在 max-tree 上把根到它的路径加一,当前点 \(u\) 在 max-tree 上的值也就是合法 \((u,v)\) 对数(\(u\gt v\)),这个过程可以 BIT 维护,复杂度依旧是 \(O(n\log n)\) 的。

记录

12. Communication Towers

考虑把每条边的区间打到 seg 上做 seg 分治,这样我们需要支持这样一个结构:支持合并,撤销,将 \(1\) 所在的连通块的答案全部设为 \(1\)

容易想到启发式合并并查集来解决,但是这个操作比较麻烦。

考虑用 fhq-treap 来维护每个连通块的所有点,这样的话合并撤销都可以在 \(O(\log n)\) 的时间内完成而不是 \(O(\min\{x,y\})\)

最后的操作也就是在根节点打一个 tag 就做完了。

时间复杂度 \(O((n+m)\log^2 n)\),但似乎大家都不用 fhq-treap,感觉是我 naive 了。

记录

13. Difference Sum Query

对着坐了两个小时终于想出来了。

我们首先来研究一个确定的 \(x_i\) 是如何生成的,注意到 \(a_i\le 2b_i\) 以及 \(b_i \le 2a_i\) 的限制,我们可以把 \(b_i\) 替换成 \(2a_i\) 得到 \(\frac{L+2R}{3}\) 以及把 \(a_i\) 替换成 \(2b_i\) 得到 \(\frac{2L+R}{3}\)。总而言之,每次递归的话规模大概会少 \(\frac{1}{3}\) 至少,所以只有 \(O(\log n)\) 次迭代。

考虑 \(x_i\) 不会再有更良好的性质了,现在让我们考虑询问的特殊性质。

这个操作的过程实际上是唯一对应一颗 BST 的,也就是如果我们得到 \(m\) 则以 \(m\) 为根递归下去构建 \([1,m)\)\((m,n]\),很重要的一点是这棵树符合 BST 性质。

首先 \(|x_i-x_{i+1}|\) 也就是两点在 BST 上的深度差,但其实其等价于两点在 BST 上的路径距离。我们注意到这个东西和求 \(L\sim R\) 构成的虚树大小有很大的联系。

因此 \(ans + dis(L,R) = 2\times size\),其中 \(size\) 是虚树边数,\(ans\) 是我们要求的东西。

首先考虑 \(dis(L,R)\) 怎么求,容易 \(O(\log)\) 求出二人的深度,两人同时做,第一次分叉的位置,前一个就是 lca。

考虑 size 不好求,转为求点数(然后减一就是 size)。最开始会认为就是 \(R-L+1\),但对着样例一画出来发现不是,在每个点爬到下一个的过程中确实会有 \(\lt L\)\(\gt R\) 的点,比如如果 \(L\)\(L-1\) 的右儿子就会出现这种问题。

还是由于 BST 的良好性质,这类点完全被 \(L\)\(R\) 两条根链所囊括,同样可以 \(O(\log n)\) 计算(但是要注意去掉 lca 之上的)。总体时间复杂度 \(O(q\log n)\)

记录

14. 屠龙勇士

考虑最后可以转成若干个 \(k_i x\equiv a_i\pmod{p_i}\) 的方程组,考虑用 excrt 解决。

具体地,当我们算出前 \(i\) 个方程的解的时候,调整量并不是 \(p\)\(lcm\),而是 \(\frac{p}{\gcd(p,k)}\)\(lcm\),设 \(lcm\)\(L\),则通解为 \(nL+b\) 的形式。

那么带入到第 \(i+1\) 个方程也就是 \(k(nL+b)=a_i\) 也就是 \((kn)L=a_i-kb\)

然后 exgcd 一下就好了。

运算中间可能会爆炸,所以用了 __int128。

记录

15. Replace All

考虑没有问号怎么做。

如果两个人的开头/结尾是一样的可以删掉,最后肯定一个人开头是 A 一个人开头是 B。

不妨假设 \(|B| \le |A|\),那我们给第二个人塞 \(B\),有两种情况:

  • 塞满了,这分两种情况:第一种是 \(B|\)\(|A|\) 的约束,这种就很优美;第二种是你还留了一个前缀,也就是剩下一个长度为 \(|A| \bmod |B|\) 的前缀(属于 \(|B|\))。

那么 \(A\) 就等价于 \(\left\lfloor \frac{|A|}{|B|} \right\rfloor\)\(|B|\) 拼起来再接一个 \(|A| \bmod |B|\)\(B\) 前缀。

  • 塞了 \(k\) 个没塞满,换了 \(A\) 接上去,则 \(A\) 有长度为 \(k|B|\) 的周期且我们知道前 \(k|B|\) 个字符的构成就是 \(k\)\(|B|\) 拼起来,那得到和上面相同的形态。

那我们把 \(A\) 全部做上面形式的替换,然后还是把开头或者结尾一样的删掉,不断重复这个过程直到 \(|A|\)\(|B|\) 的约数。(或者 \(|B|\)\(|A|\) 的约数)

则我们会发现两个串的串长在不断辗转相除,因此得到结论:除非两个串完全一致,否则它们都存在长度为 \(\gcd(|A|,|B|)\) 的周期。

那么我们只要让两个串长度相等就好了。

首先考虑上下 \(A,B\) 数量都相等,这个更简单,就是:\(\sum_{i,j\le n}2^{\gcd(i,j)}\),容易莫反在 \(O(n\ln n)\) 的时间内解决。

这里有一种特殊情况:如果两个串完全一致,则任意取两个串都是合法的,也就是 \((\sum_{i=1}^{n}2^i)\) 的平方。

然后考虑 \(A,B\) 数量不相等的情况,设第一个串的 \(A\) 减去第二个串是 \(da\) 个,第二个串的 \(B\) 减去第一个串是 \(db\) 个,则有 \(da|A| = db|B|\)

\(da=db=0\) 的时候也就是上下 \(A,B\) 数量都相等的情况;否则显然 \(da\times db\gt 0\),我们把 \(\frac{da}{db}\) 约成最简形式。然后枚举 \(|A|\)\(|B|\)\(\gcd\),则显然 \(|A|=db\times g,|B|=da\times g\),贡献是 \(2^g\);于是 \(g\) 确定了 \((|A|,|B|)\) 就唯一,所以答案是 \(\sum_{i=1}^{\frac{n}{\max\{da,db\}}}2^i\),预处理后,任意给出 \(da,db\) 都可以在 \(O(\log n)\) 的时间内求得答案(瓶颈在于约分)。

现在来考虑带问号的情况。

首先设第一个串总共有 \(x\) 个问号,第二个串有 \(y\) 个问号,我们可以写出一个 \(n^2\) 暴力,枚举两个串分别有多少个问号替换变成了 \(A\)

\[\sum_{i=0}^{x}\sum_{j=0}^{y}F(da+i-(y-j),db+(y-j)-(x-i) )\times \dbinom{x}{i} \dbinom{y}{j} \]

其中 \(F(x,y)\) 的意义是 delta 分别是 \(x,y\) 的时候的答案,也就是我们上面讨论的三类(注意到情况二难以处理,我们不妨全部视作情况一,最后去掉不正确的部分重新算上去即可,因为计算两个串完全相等的方案数非常容易)。

我们发现那个 \(F\) 化简一下也就是 \(F(da-y+i-j,db+y-x+i-j)\),也就是只和 \(i-j\) 有关。

因此我们简化一下思路,把 \(G(d)\) 设为当 \(i-j=d\) 的时候对应的 \(F\) 值,则有:

\(\sum_{d=-y}^{x}G(d)\times \sum_{i-j=d}\dbinom{x}{i}\dbinom{y}{j}\)

考虑后面的组合式非常像范德蒙德卷积:把求和变成一元的形式:\(\sum_{j=0}^{y}\dbinom{x}{d+i}\dbinom{y}{j}\)

根据范德蒙德卷积的变种形式:\(\sum_{k}\dbinom{x}{n+k}\dbinom{y}{m+k}=\dbinom{x+y}{x-n+m}=\dbinom{x+y}{y-m+n}\) 可以得到上式就是 \(\dbinom{x+y}{x-d}\)

也就是 \(\sum_{d=-y}^{x}G(d)\dbinom{x+y}{x-d}\) 即为答案。

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

记录

16. Optimal Path Decomposition

感觉这个题做起来比 diff 表现的要难一些。

考虑二分答案,设上限为 \(k\)

\(f(u)\)\(u\) 子树在满足限制的情况下,从 \(u\) 出发的所有向下的链,跳链次数最多的一条的次数最小值(让最大的尽可能小)。

转移有三种:\(u\) 是一个新开的颜色,\(u\) 和一个儿子的顶端合并,\(u\) 和两个儿子的顶端合并。

我们发现这样的话,状态不足够实现转移:因为如果 \(u\) 和某个儿子合并,则这个儿子的顶端所属颜色必定是以 \(v\) 为根的链(而不是两条)。

因此设 \(f(u,0/1)\)\(1\) 表示我们强制要求 \(u\) 最多和一个儿子合并,\(0\) 表示我们不做要求。

转移的时候:约束是最长的路径不超过 \(k\),给答案的贡献是 \(\max (f_v + 0/1)\)(如果合并了,就不需要加 \(0\))。

第一种最为容易:儿子全部选 \(f_{v,0}\) 即可,最长的路径显然是最大的两个 \(f_{v,0}\) 相加再 \(+1\)

第二种也相对简单:我们直接枚举选哪个儿子即可,它使用 \(f_{v,1}\),其他人用 \(f_{v,0}\)。只需要求前/后缀里最大/次大的 \(f_{v,0}\) 即可计算最长路径和贡献。

第三种较为复杂:我们直接枚举只能做到一个 \(O(deg^2)\) 的复杂度,会被菊花图卡上天。

考虑求路径的过程比较复杂,因此也不太会是 DS 来优化一些过程。

观察到:\(f(v,0)\) 最大的两个人至少有一个被合并,否则最长路径长度 \(\ge f(v1,0)+f(v2,0)+1\) 且向上贡献 \(\ge f(v1,0)+1\),不如第一种转移来的优秀。当确定了两个人中的一个后,就变成了 case2。

这样,我们可以用 \(O(n)\) 的时间内做一次完整的 dp,时间复杂度 \(O(n\log n)\)

注意到,根据 hld,答案其实是不超过 \(2\log n\) 的,因此进一步时间复杂度为 \(O(n\log\log n)\)

记录

17. Hungry Cow

我本来以为你还有什么高妙做法.jpg

考虑有一个部分分是 \(a_x\) 只增加不下降。考虑这个怎么做。

直接用 set 维护所有连续段就好了。这样是 \(O(n\log n)\) 的。

考虑带 \(a_x\) 下降怎么做,那就线段树分治掉。

问题是连续段这个东西的复杂度是均摊的,而线段树分治依赖的是撤销,是不允许出现这样的均摊复杂度的。

考虑用 fhq-treap 维护所有连续段,相当于是通过分裂把一部分抠出来,然后创建一个新的节点,把三部分拼在一起。

这样撤销的复杂度就对了。

关于 fhq-treap 的撤销,我们必须对过程中分出来的 \(O(1)\) 颗树都开一个专门的指针去指代它们。反正还挺麻烦的。

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

记录

18. Reversing and Concatenating

看着很 hard,其实非常 ez。

发现 \(k\gt \log n\) 的时候没什么意思:我们可以花一次操作让最小的字符(以下认为是 \(a\))放在末尾,然后操作一次,末尾的连续段长度就翻倍,然后翻最多 \(\log n\) 次就赢麻了。

当然,\(k\le \log n\) 的时候没有特别好的性质,重点是我们上面发现的这个过程:我们会让 \(a\) 作为前缀尽可能多,也就是让 \(k-1\) 次操作后 \(a\) 这个后缀尽可能长。

枚举第一次操作的方案,然后后面就贪心地把末尾长度拓展到 \(2\times k\),最后一次操作再把这 \(2\times k\) 个作为前缀,时间复杂度 \(O(n^2\log n)\)

记录

19. Shik and Copying String

\(f_i (f_i\le i)\) 表示 \(t_i\)\(s_{f_i}\) 变来,考虑 \(f_i\rightarrow i\) 的连线段,若两连线段是严格包含关系则一定无解。

另一方面,考虑两条线段有交,那我们就不能同时走完它们,只能先走靠右的线段,第二次走靠左的线段。

结合上述两点,可以得到最优的匹配:倒着枚举 \(t_i\),找到小于等于 \(f_{i+1}\) 的位置中最大的 \(j\) 满足 \(s_j=t_i\)\(f_i\) 就是 \(j\)。如果找不到,也是无解。

现在我们得到了最优匹配,让我们来更进一步研究操作次数。

首先有一个 \(O(n^2)\) 的做法:考虑一条线段 \(f_i \rightarrow i\)(如果一个点连了多个 \(i\),只用考虑最大的 \(i\)),相当于是在 \(f_i\) 这个位置放一个球滚到 \(i\)

因此我们每次考虑剩下的所有还没滚到目的地的球。不断拿出最靠右的球把它尽可能往后滚。如果滚到一个位置,该位置在这一轮已经被某个球经过(开始停留也算),则在它前一个位置停下来。

由于每一轮至少一个球滚到目的地,所以模拟轮数不超过 \(O(n)\)

如果每个球的目的地都是 \(n\),考虑球 \(i\) 会在 \(i+1\) 的出发地之前停住一次,然后所有 \(i+1\) 这个球可能停的位置,\(i\) 都会停。

因此球 \(i\) 会停在 \(pos_j - (j-i)\) 这个位置一次,其中 \(pos_j\) 是第 \(j\) 个球的出发位置,设这个集合是 \(S_i\)

回到原问题,由于目的地有单调性,所以球 \(i\) 停的位置是 \(i+1\) 的一个前缀。换言之其实真正停的位置是 \(S_i\) 的一个前缀,我们二分找到最后一个 \(pos_j\)(其实也可以双指针)即可。这样我们算出了每个球停的次数,也就能算出模拟轮数的最大值。

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

记录

20. Rearranging

后手拿到序列后,如何让序列字典序最大?

虽然可能交换一次相邻的互质的数不优,但可能再交换一次就赢了,比如 \(2,4,3\)

注意到若两个数互质则相对顺序不改变,因此对于所有的这样的数对可以建出偏序关系,显然得到的是一张 DAG,后手能得到的等价于这张图的拓扑序。

先手的目标是定向,让后手得到的最大拓扑序最小。

考虑每个连通块是独立的。对于一个连通块如果定出了若干个无入度点,后手会选最大的;从这个角度来看,先手只会让最小的点成为唯一的无入度点。

然后就做完了:从这个点开始 dfs,按照 dfs 序确定偏序就是最优方案,最后模拟后手拓扑一遍即可。

Bonus:数据范围可以开到 \(n\le 10^5\),由于只关注质因子,所以对每个质因子建虚点即可。

记录

21. rprmq1

rp rmq。

考虑把所有的修改和询问都线段树分治掉(在 \(x\) 这一维),把一个询问拆成 \(O(\log n)\) 个区间,每个区间都有两种东西:一个是祖先上挂着的点,一个是子树内挂着的点。

祖先上挂着的点比较容易,由于祖先代表的区间一定包含当前区间,我们直接在 \(y\) 这个维度区间修改,求区间 max 就好了。

子树内挂着的点可以全部取出来,均摊是取 \(O(m\log n)\) 次的,这里我们相当于是有特殊性: \(x_1\)\(x_2\) 就是取满了上下界(也就是可以认为 \(x_1=1,x_2=n\))。

我们直接把每个修改分别挂在 \(l\)\(r+1\),在 \(y\) 这个维度区间修改,最后整颗线段树在 \(y\) 对应区间上的历史最大值就是答案。

这样的话,复杂度是 \(O((m+q)\log^2 n)\) 的,既然 \(m\) 远小于 \(q\),那也就说明 \(q\) 这里不应该是两只 \(\log\)(注意到历史最值线段树常数很大)。

我们考虑这个过程不太能再加速求,但是我们可以在 \(O(\log)\) 个区间这里做手脚,考虑猫树分治,这样每个区间只问一次。

关键性质是,不指定 \(x_2=n\),只指定 \(x_1=1\) 也可以在相同的时间复杂度(单 \(\log\))用历史最值解决,只要在扫到 \(x_2\) 的时候问历史最值就好了。

因此我们每次分成两部分:\([l,mid]\)\((mid,r]\),每个挂在这个节点的询问都可以拆成这两部分。以左半部分为例,现在所有的查询都有 \(x_2=mid\),因此套用上面的做法即可;第右半部分也就是所有的查询都有 \(x_1=mid+1\)

时间复杂度降低为 \(O(m\log^2 n + q\log n)\)。实现的时候,有一个问题:我们需要撤销若干次操作。

一种思路是,直接记录下所有操作然后撤销,但这样空间复杂度会上升。

考虑我们把所有加的数,加上它的相反数,然后只需要做一次重置:将每个位置的历史最大值设为当前最大值。这可以用 lazy 实现。

也就是一个节点除了接受一个二元组加法的操作外(加数,历史最大加数),还接受一个重置的操作,这个操作首先把这个点的加法懒标记推下去(因此注意特判叶子,或者开 \(8\) 倍空间),然后将当前点的历史最大值设为现在的最大值,且清空加法标记。

在 pushdown 的时候,优先将重置标记下传,然后考虑将加法标记下放。

卡常。几个剪枝:

  1. 最后在外层线段树 dfs 的时候,若当前节点子树内没有询问直接 return。

  2. 询问历史最大值的时候,记录曾经已经询问到的答案,若当前点的历史最大值已经小于曾经的答案,就不继续递归下去了。

  3. 在每个节点处理的时候,从 \(mid\) 扫到 \(L\),也从 \(mid+1\) 扫到 \(R\),考虑如果一个修改,它过于靠左以至于最靠左的询问都完全在它右侧(右边同理)就不加入这个修改了。

还是卡常。

记录

22. Counting Good Arrays

本题是 ARC116C 的加强加强版,首先将 \(len=n\) 改成了 \(len\le n\),其次将 \(n,m\) 扩大到了 \(10^9\)

考虑 \(n\) 确定时的做法:枚举最后一个元素 \(d\),将 \(d\) 分解质因数,然后每个质因数独立,设一个质因子出现了 \(c\) 次则贡献为 \(\dbinom{n+c-1}{n-1}\)

此时我们可以容易地将 \(m\) 扩展到 \(10^9\),注意到 \(f(d)\) 是积性的且 \(f(p^c)=\dbinom{n+c-1}{n-1}\) 以及 \(f(p)=n\),可以用 min-25 筛求出答案。

现在我们要对 \(1\sim n\) 的答案求前缀和,这类一般容易想到插值。

具体地,我们发现 \(f(p^c)\) 是关于 \(n\)\(c\) 次函数,显然 \(F(n)=\sum_{1\le d\le m} f(d)\) 是关于 \(n\) 的不超过 \(\log_2 m\) 的函数,因此 \(F\) 的前缀和 \(S(n)\) 是关于 \(n\)\(\log_2 m+1\) 次函数。

也就是我们差不多只算 \(1\le n\le 32\) 的答案,然后插值就好了,时间复杂度 \(O(m^{\frac{3}{4}})\)(本来下面除了 \(\log\) 的,但是做了 \(\log\) 次 min-25 筛)。

记录

HDU 啥时候换你那评测机啊。

23. 交通堵塞

停在第 \(i\) 个灯的概率是过掉前 \(i-1\) 个灯的概率去掉过掉前 \(i\) 个灯的概率。所以研究的可以认为是:给出若干个灯问全部通过的概率。

由于 \(2019!\) 非常大,可以看作在 \([0,\infty)\) 上随机一个时刻。

由于无穷多个点,所以所有离散的整点的答案都可以忽略。

考虑每个 \((i,i+1)\) 段内的点,它们的答案都是相同的。

\(M=lcm(m_1,m_2,..,m_n)\)(其中 \(m=r+g\)),则每隔 \(M\) 段一个循环。枚举 \(0\le b\lt M\),可以认为时刻 \(M\) 出发,在时刻 \(M+x_i\) 到达第 \(i\) 个灯,看 \(M+x_i\)\(m_i\) 是否大于等于 \(r_i\) 就知道能不能过了。

模拟的时候,有一个部分分是 \(m_i\) 两两互质。注意到对于互质的两个数 \((x,y)\)\(kx+b\)\(0\le k\lt y\))刚好构成模 \(y\) 的完全剩余系。

总而言之,最后对于每个 \(m_i\),它有 \(g_i\) 个时刻合法(随便选 \(g_i\) 个时刻就行,不用连续),则把 \(g_i/m_i\) 乘起来就是概率了。

考虑一般地情况,也就是把形式转成 \(m_i\) 两两互质。

考虑每个人把唯一大于根号的质因子拿出来作为代表(没有就是 \(1\)),这样的话就能回到上面的情况。

考虑定一个数 \(V\),枚举出发时刻模 \(V\) 的余数 \(b\),计算所有的 \(kV+b\) 对答案的贡献。

\(k\) 的取值是无穷大,但对于一个人 \(i\) 来说,循环节就是 \(\frac{m_i}{\gcd(m_i,V)}\)

因此我们把每个人的 \(2,3,5,7\) 都用 \(V\) 消掉就好了,这样的话 \(V\) 要取 \(2^6\times 3^4\times 5^2\times 7^2\),而复杂度是 \(O(Vnw)\) 的,其中 \(w=100\)

除了两个人互质,还有一种情况比较好算:就是一个 \(m_i\) 是另一个 \(m_j\) 的倍数,那把 \(m_j\) 重复做 \(\frac{m_i}{m_j}\) 次,然后合并两个人的共同合法时刻就好了。

所以我们不一定要把 \(2,3,5,7\) 都消掉。考虑写个程序找到最小的 \(V\) 使得算出新的循环节以后,任何两个人要么互质要么是倍数关系。

找出来是 \(2520\),这个数很熟悉,就是 \(1\sim 10\) 的 LCM。

为什么是这个数,首先考虑一个数的唯一分解,至多只有一个 \(p^a\) 是大于根号的,而只有这个 \(p\)\(V\) 约去后还会留下来,其它的肯定是都不留。(但这样得到的比我们想要的性质更强,因为如果两个数互为倍数则都是形如 \(p^a\) 的,而我们其实不需要这个,只需要是倍数就好了。)

所以应该从 \(2^3\times 3^2\times 5\times 7\) 的视角来看我们得到的 \(V\),最后得到的自然是 \(2520\)

此时 \(O(Vnw)\) 就可以接受了。

记录

24. Timber

我们随便生成 \(m\)\(k+1\) 的线段,把剩下的 \(n-m(k+1)\) 个点分配进 \(m+1\) 个空,然后对每条线段有 \(2\) 种方式决定它的方向。这样每种方式都对应一组合法解,但是一组合法解会被算多次。

考虑这样一个贪心地判定解合法的过程:从第一棵树开始能往左倒就往左倒,否则往右倒。

考虑把每组解的代表方式设定为上面这种贪心解得到的方式,也就是对于每条线段,如果你设置它是朝右的,那么它和上一个人的空不能 \(\ge k\)

也就是抽象成了 \(m\) 个条件全部不能被满足的方案数,对这个做容斥即可,时间复杂度 \(O(n+m)\)

记录

25. 小埋与游乐场

Warning:请忽略 \(A,B\) 中所有的 \(0\)

然后我们考虑 \(A\oplus B\),如果 \(A\) 的 lowbit 更大则会变成 B 的 lowbit,否则 lowbit 不变。

所以通常情况下我们会选择一个 lowbit 更小的数 xor 上去,还有一种情况是 A=B。

注意到部分分就有 A,B 没有相同的数,这种情况下我们按照 lowbit 从大到小枚举 A 中的数,然后找到当前 B 中 lowbit 最小的数 xor 上去就行了。

对于有相同的情况,考虑这样的贪心:枚举顺序不变,如果当前数在 B 中出现过了,就选它;否则就选 lowbit 最小的。

这个是正确的原因是,设当前数是 \(u\),显然如果有 \(u\) 你就选,否则你去选了一个 \(v\),如果 \(A\) 中没有 \(v\),那直接匹配 \(u-v\) 显然是最优秀的;否则我们假设 \(v-v\) 匹配,那 \(u\) 只会和一个 lowbit 更大的 \(v'\) 去匹配,这样的话 \(lowbit(u)-lowbit(v') + lowbit(v)\) 是没有 \(lowbit(u)-lowbit(v)\) 大的,也就是你的减数没有更优秀。

当然,可能会有 \(lowbit(v')=lowbit(v)\) 的情况,这种情况下前一种情况反而优,所以如果有多个 lowbit 最小的数,我们优先去选择不在 A 剩下的数里出现过的,然后再去选再剩下的 A 中出现过的。

同理,枚举 A 的时候,在 lowbit 最大的情况下,优先考虑在 B 中出现过的,其次考虑在 B 中没有出现过的。

时间复杂度 \(O(n\log n)\),瓶颈在排序。

记录

26. Visual Python++

考虑矩形问题用扫描线解决:我们在 \(x\) 这一维扫描线,当枚举到一个右下角的点时,我们会选择 \(y\) 坐标不超过它的最小的 \(x\) 坐标的点(如果有多个,会发现其实是无解的,所以可以随便选)。

这样最后只需要判定一次。

判定的话,相当于是对每个边框看有没有线段和它相交(这里要注意退化成一线段的情况)。

考虑如果是两条水平(或者两条竖直)线段相交是容易的,把 \(y\) 坐标相同的水平线段拿出来然后看是否有两条有交即可。

对于十字型的,我们依旧考虑扫描线,对每条水平线段,记录 \(y\) 这个点在进入和出去的时候的值的差;扫到竖直线段的时候把 \(yL\sim yR\) 区间的位置都加一(就算这里退化了,也要加入四条边进去)。然后每条水平线段应该恰好差为 \(2\)(其所在矩形的两条竖直线段带来的贡献)。如果大于 \(2\),说明有十字交叉,也是无解。

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

记录

27. 有向图

其实是保序回归的 \(L_1\) 问题板子。结论是首先,每个点的最终值 \(y_i\) 都落在 \(\{d_1,d_2,...,d_n\}\) 这个集合内,然后我们每次钦定 \(a\lt b\),当成只有两种点值(\(a,b\))可以选择,此时得到的最优解;则一定存在一组真正的最优解,满足 \(=a\) 的点都 \(\le a\),而 \(=b\) 的点都 \(\ge b\),所以整体二分,取 \((a,b)=(mid,mid+1)\) 即可。

注意到本题是树 / 基环树。如果是树,我们直接设 \(f(u,0/1)\) 就好了;如果带环,则枚举环上某个点的取值,然后就可以破环了。

时间复杂度 \(O(n\log n)\),不过常数很大,不知道佳怎么写的那么快。

记录

28. 操作序列计数

好题,然而要高精度很恶心。

考虑枚举 \(L\) 然后计算,注意到 \(L\le \log n\)

则首先 \(n\) 减去 \(k^L\),然后问题变成了有多少个 \((k^0,k^1,...,k^L)\) 的线性组合能组合成 \(\le n-k^L\) 的非负整数。

我们以下令 \(n=n-k^L\),且将其转成 \(k\) 进制。

首先 \(\le n\) 不如变成 \(=n\),只要给 \(k^0\) 那里多添一个,也就是变成 \((k^0,k^0,k^1,k^2,...,k^L)\) 的线性组合 \(=n\) 的个数。

这个问题,乍一看是不弱于背包的,然而注意到这个向量的特殊性质,全部是 \(k^i\),我们把系数 \(c_i\) 也全部写成 \(k\) 进制的形式。

这样,原本可能某个 \(k^i\) 的系数是 \(O(n)\) 级别的,但现在我们把每个 \(c_ik^i\) 拆成了若干个 \(d_jk^j\) 的形式且每个 \(d_j\)\([0,k)\) 之间。

注意到一共只有 \(O(\log L)\) 个系数,因此最后每一位的值不超过 \(O(k\log L)\)

这个时候我们把每一位 \(\ge k\) 的部分往前进位,则前 \(i\) 位向第 \(i+1\) 的进位不超过 \(2\log n\)

因此可以设:\(dp(i,j)\) 表示我们考虑完了前 \(i\) 位,向第 \(i+1\) 位的进位是 \(j\) 的方案数。

转移的时候,也就是 \(dp(i,j)\rightarrow dp(i+1,k)\) 的时候,有 \(\min\{i,L\}+2\) 个系数等着我们确定新的一位的取值,我们枚举他们在 \(i+1\) 这位上的系数和 \(S\),则需要满足 \((S+j)\equiv n_{i+1}\pmod k\),又因为 \(S\)\(O(k\log L)\) 的,所以每次转移需要枚举的 \(S\) 只有 \(O(\log L)\) 个。

转移的时候,我们要把 \(S\) 个数分配给 \(\min\{i,L\}+2\) 个变量使得每个变量的值都在 \([0,k)\) 之间。这可以通过插板 + 容斥预处理。

时间复杂度 \(O(\log^4 n)\),理论上 \(k\) 可以 \(10^9\)。(只要输入愿意用 \(k\) 进制形式给出)

记录

29. Double Palindrome

称一个串是本原的当且仅当其有唯一方式拆分成两个回文串(如果本身回文,也算一种拆分方式)。

若一个串 \(S\) 有两种拆分方式,则其一定是由一个本原串复制若干遍而得到。

证明:设两种拆分方式分别拆分了前 \(p,q\)\((p\gt q)\),注意到 \(s[q]\)\(s[p]\) 的回文 border,因此 \(s[p]\) 有周期 \(p-q\)。令 \(d=p-q\),我们从 \(s[p]\) 开始往前,每隔 \(d\) 个划分一次,最后余下 \(r=p\bmod d\) 个字符,这部分 \(s[r]\)\(s[p]\) 的末尾 \(r\) 个完全一致(因为是周期),同时 \(s[p]\) 回文所以 \(s[r]\) 还和 \(s[p]\) 末尾 \(r\) 个取反完全一致。所以 \(s[r]\) 回文。

同理,串 \(s\) 结尾的 \(d-r\) 个字符构成的串也是回文的。

然后这两个串拼在一起得到的串 \(t\) 复制若干遍就得到了 \(s\)

如果 \(t\) 还有其它的拆分,递归地,我们可以继续把 \(t\) 拆分成一个本原串 \(r\) 复制若干遍的结果。如果我们挑选的是一个串 \(p,q\) 最小的两种拆分方式,而得到的 \(t\) 不是本原的,则把它展开写成 \(r...r\) 的形式再用这个东西去替换 \(s\),会得到一对更小的拆分方式,这是矛盾的。因此 \(t\) 的拆分方式存在且唯一。因此 \(t\) 本原。

\(f(n)=\sum_{i=0}^{n-1}k^{\frac{i}{2} + \frac{n-i}{2}}\)(上取整),而 \(g(n)\) 是长度为 \(n\) 的本原串数目。

\(f\) 可以 \(O(n)\) 求,\(g(n)=f(n)-\sum_{d\mid n,d\lt n}g(d)\times \frac{n}{d}\),而答案即为 \(\sum_{i=1}^{n}g(i)\lfloor \frac{n}{i} \rfloor\),时间复杂度 \(O(n\ln n)\)

记录

30. M-tree

好题,vp 的时候被我 2log 2495ms 卡过去了,很有感觉。

考虑一组询问怎么做,注意到答案有上下界 \([\max a,\max a+\log n]\),这启示我们直接枚举答案 \(v\)

然后,贪心地,我们会把较大的放在较浅的叶子。

如果两个 \(a_i,a_j\) 不相等,则它们一定不会同层。假设 \(a_i\gt a_j\),则放完所有的 \(a_i\) 后,我们可以让剩下的人全部拓展出 \(m\) 个叶子,然后把 \(a_j\) 放到下一层(深度差了 \(1\),但是值至少也差了 \(1\))。

考虑这样的一个问题是可能会出现某些叶子不放数的情况,但我们知道这种情况下答案其实还是不变的。

因此考虑如果令 \(b_i=v-a_i\) 则我们会在第 \(b_i\) 层的时候才去放 \(i\) 这个人。设 \(cnt_i\) 是有多少个人 \(b=i\) 则有 \(f(0)=1,f(i)=mf(i-1)-cnt_i\),我们不能放 \(f\) 在某个时刻是负数。

这样其实我们就能做一组询问了。

考虑多组询问,假设我们只顾拓展,拓展了 \(k\) 轮才开始在叶子放数,此时设 \(cnt_i\) 是多少个人 \(a=N-i+1\),则我们是想让:\(f(1)=m^k-cnt_1,f(i)=mf(i-1)-cnt_i\) 永远非负。

把这个展开,也就是 \(m^{k+n-1} \ge (m^n cnt_1 + m^{n-1} cnt_2 + ... + cnt_n)\)

这个就是一个 \(m\) 进制数,也就是 \(a=x\) 就相当于在第 \(x\) 位加一,修改就是先减再加;问的就是位数 + 1。

特殊情况是,如果这个数形如 \(1000000...000\),注意到是取到等号的,答案要减一。

然后这个 +1 / -1 就是维护连续段就好了,这里用了 seg 来维护,修改的时候要 seg 二分。

补个赛后写的 \(O((n+q)\log n)\)

记录

31. The Maximum Prefix

我草,真的是神题,怎么不放到 AGC ?

考虑正着要记录两个东西:最大值和当前和。

但我们可以倒着:这样的话 \(f_i=\max(0,f_{i+1} + a_i)\),而 \(f_1\) 就是前缀最大值。

我草怎么想到的。

这样如果只问长度 \(n\) 就能 \(n^2\) 了:从 \(f(n+1,0)\) 出发初始为 \(1\)\(\sum f(1,i)\times h_i\) 就是期望。

现在要问 \(n\) 个长度,那就反过来做,从 \(f(0,i)\) 出发初始为 \(h_i\),走到 \(f(i,0)\) 的答案就是对应长度的期望。

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

记录

32. Games on DAG

考虑计数不合法的图,也就是 \(sg(1)=sg(2)\) 的。

这样的话可以得到一个 Bell(n-1) 级别的做法,假设我们确定了 \(sg=0\sim i\) 的人,考虑剩下的人里,编号最大的人一定是 \(sg(i+1)\) 的,这个过程和我们枚举集合划分是一致的。

当然这个还不够快,大概只能通过 \(n=13\),考虑确定了每个人的 sg 之后,我们只用保证:sg 相同的没有连边,而一个 \(sg=i\) 的点,对于所有 \(j\lt i\),至少连了一个 \(sg=j\) 的点。

考虑 \(dp(S)\) 是我们划分了若干层 sg 后把集合 \(S\) 的全部囊括。每次加入一个新的集合 \(T\),然后所有 \(U-S-T\) 的人都和 \(T\) 中至少一个人有连边,\(T\)\(U-S-T\) 的连边可以任意连,\(T\)\(T\) 之间可以连边,通过一些预处理可以做到 \(O(n3^n)\)

记录

33. Toy

由于有旋转同构,我们考虑 burnside 引理。

对于一个环上全体偏移 \(i\) 步的置换(\(1\le i\le n\)),我们知道会构成 \(\gcd(n,i)\) 个置换环。

注意到前 \(\gcd\) 个人分别属于两两不同的置换环,把它们向下一个人的连边(包括第 \(g\) 个人向 \(g+1\) 的连边),以及中心点向它们的连边拿出来;如果确定了这些边的保留与否,就可以推出剩余边的保留与否;

若我们保留了中心点到某两个点的连边(称这样的点是特殊点),则这两个点之间需要恰好断一条边。

特殊地,我们认为这个图是成环的,也就是这个子图是 \(1\rightarrow 2\rightarrow ... g\rightarrow 1\) 的。

考虑分类讨论,是先出现特殊点,还是先出现断边。以第一种情况为例,则有 \(g(0,0)=1\)\(g(i,0)=2g(i-1,0)+g(i-1,1),g(i,1)=g(i-1,0)+g(i-1,1)\)\(g(i)\) 就是表示你确定了前 \(i\) 个点的导出子图的情况,为 \(0\) 就说明上一个特殊点的断边已经断过了,为 \(1\) 就说明上一个特殊点的断边还没有确定。答案为 \(g(\gcd,0)\)

第二种情况递推式不变,不过初值变为了 \(g(0,1)=1\) 且答案为 \(g(\gcd,1)\)

显然,其可以矩阵快速幂求。

这样我们就有了 \(O(d(n) \times \log n)\) 的做法,所有约数的欧拉函数可以 dfs 在 \(O(d(n))\) 的时间内求出。

其实可以继续卡:第一个想法是我们预处理矩阵的 \(2^i\) 次幂这样求值的时候可以砍掉 \(2\) 的常数(变成向量求矩阵)。更优秀的方法是用类似 BSGS 的方法平衡复杂度。

注意到本题 \(m\) 是任意模数,而我们有 \(\frac{a}{n}\bmod m = \frac{a \bmod{(n*m)}}{n}\),不过此时注意乘法会爆 long long。

记录

34. Build the Tower

考虑有一个 \(2^{3n}\) 的高斯消元做法,显然没有前途。

注意到,虽然有回退,但是回退回去的状态是唯一的,所以可以给 \(2^n\) 个状态建树,然后相当于在树上随机游走。树上高斯消元可以做到 \(O(2^n)\)

还是不够快。注意到我们转移的时候只关注 min 和 sum,考虑只记录这 \(nw\) 个点,然后整个图形成拓扑结构,因为一个状态可能被多个状态转移到。

\(x,y\) 都能转移到 \(z\),从 \(z\) 的视角来看 \(x,y\) 是没有本质区别的,因为都可以表示成 \(z=kx+b\) 以及 \(z=ky+b\)(这里 \(k,b\) 是不变的)。

所以按照拓扑序倒着做消元就好了,时间复杂度 \(O(nw)\)

记录

35. Yes or No

基础转化:我们考虑对 \(\dbinom{n+m}{n}\) 种答案序列的答对题数求和。注意到我们的策略只依赖于当前剩下的 Yes 和 No 总数,设为 \((x,y)\),则我们会去回答出现次数比较多的那个。

考虑这个过程类似网格图上的游走:也就是我们会靠近对角线(如果就在对角线上不妨钦定向左走)。

一个答案序列相当于 \((n,m)\)\((0,0)\) 的一条路径,其权值为“正确的”步数数量(也就是靠近对角线的那些行进,以及在对角线上且往左走的行进)。相当于求所有路径的权值和。

考虑相邻两次接触到对角线,设一次在 \((i,i)\) 一次在 \((j,j)\)\(i\gt j)\) 且中间没有再碰过对角线,则可以容易算出,如果一直在上半部分游走,这里对权值的贡献是 \(i-j+1\),如果在对角线下半部分游走,则贡献是 \(i-j\)

考虑先计数,注意到其之和 \(i-j\) 有关,事实上可以容斥 + 分治 NTT 求出方案数 \(g\),然后 \(\frac{g}{2}(2*(i-j)+1)\) 就是贡献,设这个值是 \(F_{i-j}\),则我们要乘上 \((j,j)\rightarrow (0,0)\) 的方案数以及 \((n,m)\rightarrow (i,i)\) 的方案数,这两部分是好求的,相当于是三个数组卷积,做两次 NTT 就好了。

还有一部分贡献来源于 \((n,m)\) 第一次游走到对角线,首先方案数依旧可以容斥 + 分治 NTT,这次是二元卷积,再做一次 NTT。

两部分分治 NTT 可以用求逆代替,这个做法时间复杂度 \(O(n\log n)\),需要两次求逆三次 NTT,可以看出常数很大,最后换了 acl 的 NTT 板子才通过。

记录

另解:依旧考虑网格图上的游走,不妨设 \(n\lt m\),如果我们始终不穿过对角线,不难看出答案就是 \(n\)。如果有一段穿过了对角线,把这一段翻转回来,但是每翻一次就会少了 \(1\) 的贡献(考虑对角线上往左走的这次)。所以答案是 \(m\) 加上每个 \((i,i)\rightarrow (i-1,i)\) 被经过的次数,也就是 \((n,m)\rightarrow (i,i)\) 的方案数乘上 \((i-1,i)\rightarrow (0,0)\) 的方案数,组合数算出来即可。时间复杂度 \(O(n+m)\)

记录

36. Shopping

神题。

首先相当于我们走的来回数最小。

考虑一个商店有两种可能:从左边进去和从右边进去,首先可以把 \(t\)\(2L\) 取模,这部分直接加上去就好了;然后 \(t=0\) 的商店就可以直接忽略掉了。

\(f(i)\) 是从左边进去,出来的方向是否是从右往左的;设 \(g(i)\) 表示从右边进去,出来的方向是否是从左往右的;若 \(f(i)=g(i)=0\) 则也可以忽略它。道理和 \(t=0\) 是相近的,这两部分比较自然。

比较显然的分析是:考虑一个人如果单独为它服务的话至少要花一个来回,而一个来回最多为两个人服务:设 \(i\lt j\)\(g(i)=f(j)=1\) 必须成立。

而且一个人可能一个来回还不够,如果 \(f(i)=0\) 且你从左边进去,则最后其实是一个来回后又往 \(L\) 走了一小段才能接到你,不过注意到这一段是可以和下一个人的路径合并在一起的。所以基本可以认为一个人给他开一个回合就能赢。特殊情况是最后一个人。

所以我们的来回数有一个上界:\(n+[f(n)=1]\)

我们知道一个回合带两个人的唯一方式就是上面提到的那种;这样的话我们可以 \(0\rightarrow L\) 的时候先不把人放在 \(i\),放在 \(j\),回来的时候接上他然后放在 \(i\),然后这轮其实结束了,但是人还在 \(i\) 呢,我们处理下一个人的时候再去接上他。每次匹配一对答案就减少一。

这样的话我们有一个 \(O(n^2)\) 的做法,就是类似括号匹配,然后设 \(f(i,j)\) 表示前 \(i\) 个车站处理完还有 \(j\) 个左括号没匹配,在此之前已经匹配的最大个数。

最后这个性质就比较难想:注意到 \(f(i)=1,g(i)=0\) 的位置只会在右半边;而 \(f(i)=0 g(i)=1\) 的位置只会在左半边。相当于是:左括号,右括号,通配符;只考虑前两种的话形态是 \())))((((\),所以先拿左括号和通配符,然后是右括号和通配符,剩下的通配符两两匹配就可以。

这样就 \(O(n)\) 了,太酷辣;但其实有一个坑点就是匹配的时候 \(n\) 是不能参与进去的:大概就是说,如果你匹配了 \(n\),那你考虑最后一次经过的商店,如果它能作为右括号那你还不如现在这次就和它匹配;否则这个人是要比原本多转一次的(原本 \(f(n)=1\) 少算了一次)。

其实感觉比较妙的点是,虽然一个人只用一个来回可能解决不了,但只要后面还有下一个人,那他就可以利用下一个人这个来回的开始那一段。

记录

37. Counting of Subarrays

考虑如何去判定一个序列是否合法:显然长度为 \(1\) 的必须合法;否则它一定是若干个 \(\ge L\) 的序列合并而来的。

如果 \(k=2\),则其一定是 \(\ge L\)\(1\) 合并而来的;然后考虑 \(k=3\):我们的序列有 \(1\)\(2\),考虑每个 \(2\) 都是一个 level\((2,L)\) 的,只用考虑所有的 \(1\) 连续段:设长度为 \(x\) 则其可以构成 \(\lfloor\frac{x}{L}\rfloor\) 个连续段...... 以此类推,我们得到这样一个过程:当 \(\min\neq \max\) 的时候,考虑 \(\min\) 的所有连续段,设长度为 \(x\),若长度 \(\lt L\) 则不合法,退出;否则换成 \(\lfloor \frac{x}{L} \rfloor\)\(\min+1\)。最后若剩下的个数 \(\ge L\) 个就是合法的。

考虑我们从小往大枚举 \(x\),然后计算多少个合法子区间(长度 \(\gt 1\))且它的 \(\max=x\)

\(x=1\) 的时候,考察每个连续段,所有长度 \(\ge L\) 的子区间都是合法的;然后我们考虑 \(x=2\) 的时候,我们就要把所有 \(1\) 的连续段合并了:如果连续段长度 \(\lt L\) 那么设这段位于 \([L,R]\),则后面的子区间都不能和它有交(也就是从这一段开始断开,两边分开来考虑)。否则考虑就替换成 \(\frac{len}{L}\)\(2\)

注意到比如 \(L=2\),那么 1111 a 换成 22 a 以后,比如 2a 这个子序列,其实它的左端点可以是 \(2\) 也可以是 \(3\)(但不能是 \(1\) 或者 \(4\))。也就是一个人可能对应了多个左端点(同理也会对应多个右端点)。所以我们记录 \(L,R\) 表示这两个东西,初始 \(L=R=1\)

合并的时候更新一下 \(L,R\) 就好了。算贡献的时候用前缀和算一下。不过需要注意的是可能 \(x-1\) 算过的又会在 \(x\) 这里算一遍。所以需要记录每个 \(x\) 是下面合并上来的,还是本来就是 \(x\)

由于合并一次至少除以 \(L\) 所以时间复杂度 \(O(n\log n)\)

记录

38. Flow Control

考虑若 \(n\) 个人同时加入,同时删除,则如何快速计算贡献。

如果我们从值的角度考虑均摊,则不太对,因为假设大家的上界都是 \(1\),那么不断 \(/2\) 再加 \(1\) 是寄的。也就是除以 \(2\) 的次数其实很多。

考虑加法操作不影响差分;而当极差 \(\ge 2\) 的时候,做一次 \(/2\) 差分变一半;\(\min=\max\) 也比较容易;讨论一下 \(\min=\max-1\),如果 \(\min\) 是奇数则除以 \(2\) 以后还是 \(1\);否则就是 \(0\) 了。

\(\min=\max\) 的时候,我们再加到满(设此时和为 \(lim\)),再除以 \(2\)(设此时的和为 \(sum\)),容易发现整个值随后其实就是不断地 \(sum\rightarrow lim\rightarrow sum\rightarrow lim\rightarrow ...\) 循环,所以可以 \(O(1)\) 计算出答案。

\(\min=\max-1\) 的时候,不难发现再迭代 \(O(1)\) 次(也就是顶满再除以 \(2\))后也会进入周期。

所以复杂度分析可以用势能来解决:seg上每个节点的势能操作 \(O(\log V)\) 次后就结束了;那复杂度就是 \(O(n\log n\log V)\)。而回到原题我们对时间扫描线然后要处理单点加入单点删除之类的,考虑一次单点修改可以认为是把 \(O(\log n)\) 个节点的势重置,那依旧是 \(O(\log n\log V)\) 的贡献,所以复杂度不变。

记录

posted on 2023-04-03 22:18  Cry_For_theMoon  阅读(300)  评论(1编辑  收藏  举报