Cry_For_theMoon  

字符串选做

CF1483F Exam

考虑我们枚举 \(i\):然后我们建出所有串的 ACAM,并且把 \(s_i\) 挂在上面跑。这样,对于每个 \(1\le x\le |s_i|\),我们应该都能找到(至多)一个长度最大的 \(j\),使得 \(s_j\) 出现在了 \(s_i\) 中,并且结尾位于 \(x\),这是容易 \(O(|s_i|)\) 做到的,接下来我们令 \(m=s_i\)

显然,只有这最多 \(m\) 个串可能和 \(i\) 构成需要计数的二元组。称这最多 \(m\) 个串为关键串。显然也并不是所有关键串都合法。如果对于一对不合法的 \((j,i)\)\(s_j\)\(s_i\) 的子串,那么一定存在一个关键串 \(k\) 使得 \(s_j\)\(s_k\) 的子串且 \(s_k\)\(s_i\) 的子串。换言之:我们就是在看这 \(\le m\) 个关键串不是其它关键串的子串。

一个朴素的想法是:从大到小考虑关键串,然后把它的子串都标记为不合法。如果扫到一个关键串的时候其还未被标记就有 \(1\) 的贡献。但我们显然无法做到”标记一个串的所有子串“。

注意到,每个关键串都能表示为 \(s_i\) 上的一段区间。

所以考虑变成这样一个事情:把一个区间里的所有子区间标记为不合法。换言之就是删掉那些被包含掉的区间。

但是有一个事情是,一个关键串有多次出现,因此在 \(s_i\) 上有多个区间。而它不合法应该等价于存在一个对应区间被标记了。对于一个关键串 \(s_j\),考虑其所有出现的结尾位置 \(x\)

  • 如果 \(x\) 这里导出的关键串就是 \(s_j\),则我们直接把 \((x-|s_j|,x]\) 称为关键区间。
  • 否则,\(x\) 一定不合法。因为有一个更长的 \(s\) 和它结尾相同。

所以一个关键串合法的必要条件是它的所有出现位置 \(x\),都满足位置 \(x\) 导出的关键串就是它本身。

而一共只会导出 \(m\) 个关键串,所以我们实际上只关注了 \(m\) 个关键区间是否被更大的关键区间包含。这样就做完了。

梳理一下,我们需要做两件事情:

  • 对于每个关键串 \(s_j\),检查其成为关键串被导出的次数,和在 \(s_i\) 里的出现是否相同。
  • 把被包含的关键区间删掉。

两部分都容易 \(O(m\log m)\) 完成。因此建立 AC 自动机后就在 \(O(\sum |s_i|\log)\) 的时间内解决了问题。

代码

CF1276F Asterisk Substrings

需要计数的子串,在一般情况可以被表示成 x+y 的形式(其中 + 是题目的拼接符)。

先考虑暴力:我们枚举 \(x\) 以后,确定 \(x\) 的 endpos 集合 \(S\),那么 y 的开头,只能是某个 \(S\) 中元素集合 \(+2\) 的结果。因此我们并不关注 \(x\),只关注 \(x\) 的 endpos 集合。这启发我们建立 SAM。

当确定 \(S\) 了以后,注意到我们相当于钦定了 y 这个部分的开头位置。因此,再对反串建立 SAM。

此时,\(S\) 中若存在元素 \(a\),则就是在告诉我们,反串 parent tree 中,位置 \(a+2\) 代表的叶子,到根,这条根链上的串都能作为 y 存在。也就是 \(S\) 中的每个元素对应反串结构上的一条根链。而一个 \(S\) 能用的 y 数量应该是这些根链信息并。

在原串的 parent tree 上跑启发式合并,然后问题就变成了有 \(O(n\log n)\) 次在反串的树链上加入某条根链,问链并的信息的一个数据结构问题。容易树剖后在 \(O(\log n)\) 的时间内维护。因此总时间复杂度 \(O(n\log^2 n)\),常数很小。

代码

ICPC沈阳2021 M. String Problem

lyndon是什么,没听过?

我们实质上是在求每个前缀的字典序最长后缀。令答案为 \(ans(i)\),考虑从 \(ans(i-1)\) 增量得到。

容易发现:\(ans(i)\) 一定是 \(ans(i-1)\) 的某个 border(空串也认为是 border)加上字符 \(c=s_i\) 得到。

若不是,则假设 \(ans(i)=p+c\),其中 \(p\)\(S[1...(i-1)]\) 的一个后缀。显然 \(p+c\)\(ans(i-1)+c\) 的字典序已经在 \(c\) 之前比出,又因为 \(ans(i-1)\)\(S[1...(i-1)]\) 字典序最大后缀,这就说明 \(p\) 已经败下阵来。

现在要做的是:考察 \(ans(i-1)\) 的最短的一个 border \(x\),使得 \(ans(i-1)\) 的第 \(|x|+1\) 个字符小于 \(c\)

\(m=|ans(i-1)|\)关键结论是:如果我们把 \(ans(i-1)\) 替换成了一个更短的 border,这个 border 的长度应该至少是 \(\le \frac{m}{2}\) 的。证明类比一个字符串的最短 border 若存在则一定长度小于等于一半:我们假设最后换成了长度为 \(x\in (\frac{m}{2},m)\) 之间的一个 border。首先 \(2x-m\) 一定也是一个 border,然后 \(ans(i-1)\) 的第 \((2x-m)+1\) 个字符应该和第 \(x+1\) 个字符相同。也就是说 \(2x-m\) 既是一个 border,也满足第 \(2x-m+1\) 个字符小于 \(c\)(因为第 \(x+1\) 个字符小于 \(c\) 了),那么就应该换成 \(2x-m\) 而非 \(x\)

如何运用这个结论?我们只需要可以快速支持:有一个字符串,每次给出一个字符 \(c\),然后末尾加入字符,或者查询是否存在一个 border,使得第 \(x+1\) 个字符小于 \(c\),这两点就够了。

只在末尾加的话,我们就是在做 kmp:每次加字符就是在 fail 树上加叶子。然后询问的其实是一条根链上,是否存在一个点 \(u\),使得维护的字符串的第 \(|u|+1\) 个字符 \(\lt c\),那么我们维护根链上的最小值信息就可以快速判断了。

然后当我们把 \(ans(i-1)\) 换成更小的 border 的时候,直接暴力从头重构即可。复杂度均摊 \(O(n)\)

代码

CTSC2016 香山的树

很酷的题!

题面中的串实际上是 \(\text{lyndon}\) 串,以下还是称作合法串好了。

考虑我们先留一个黑盒 \(f(p)\) 在:给出长度为 \(m\) 的串 \(p\),求有多少个合法串以 \(p\) 为前缀。

利用这个黑盒,我们可以调用 \(O(n\Sigma)\) 次来求出 \(s\) 开始的第 \(k\) 个合法串。先考虑调用 \(f(s)\),令结果为 \(k'\)

  • \(k\lt k'\),那么 \(s\) 就一定不是答案的前缀。我们把 \(s\) 变为字典序更大的东西:删去末尾的极长的 z,然后如果此时删空了那就说明无解。否则把结尾的字符自增一次。
  • 否则 \(s\) 一定是答案的前缀。先特判 \(s\) 本身是否为合法串,以及 \(k\) 是否为 \(1\)。否则把 \(k\) 减一(如果 \(s\) 本身为合法串的话)且在 \(s\) 的结尾增加上 a

接下来来考虑实现这个黑盒,也是本题的重头戏。

自身是最小表示法,以及没有循环节。两点都非常令人头疼。

第一点在我们计数的约束下可以改写为:最小表示法以 \(p\) 开头的方案数。

接下来,我们认为要计数的串 \(s\) 是首尾相接的。然后发现这个约束其实等价于:

  • \(p\) 至少出现一次。
  • 每一个 \(|s|\le n\) 个长度为 \(m\) 的子串,字典序都大于等于 \(p\)

充要性显然。我们发现:若 \(s\) 不存在任意循环节,则假设 \(p\) 出现了 \(k\) 次,这个串就会被算 \(k\) 次。但是应该只有 \(1\) 次是我们想要的。

如果有循环节:你假设循环节长度为 \(x\),在这个循环节(循环节也被认为是首尾相连的,其实这里有点 burnside 的味道,毕竟去掉循环同构本身也是可以 burnside 的,没有本质区别。)里 \(p\) 出现了 \(y\) 次。则得有 \(y\mid p\)。然后你发现这个串会被算 \(y\) 次,我们也是只想要 \(1\) 次。

所以我们考虑容斥:假设能算出 \(f(i,j)\) 表示长度为 \(i\) 的串且 \(p\) 出现了 \(j\) 次。那么就容易通过容斥把循环节去掉,最后 \(f(i,j)\) 除掉 \(j\) 后再去贡献给 \(ans_i\) 即可。

至于算 \(f(i,j)\) 可以考虑 dp:我们令 \(dp(i,j,k)\) 表示考虑完了前 \(i\) 个字符的填法,然后 \(p\) 已经出现了 \(j\) 次,当前和 \(i\) 的 border 长度为 \(k\)(这里先不认为是首尾相连的,毕竟你还要添加字符进去)。容易发现这个其实就是在 kmp 自动机上走啊啊,所以加一个字符是否合法,以及新的 \(k\) 都容易预处理出。当我们把 \(dp(i,j,k)\) 贡献给 \(f(i,j)\) 的收再来考虑首尾相接的问题:这里的贡献值只和 \(j\) 有关,也可以预处理出来。

实现的时候需要注意一些细节,这个题的具体细节应该会有一些不同的实现,大家可以自信,反正我的细节和任何一篇题解都不同。。

时间复杂度的话,一次算 \(f(p)\)\(O(n^3\Sigma)\) 的,总复杂度就是 \(O(n^4\Sigma)\) 但远远跑不满。不过有些说法是算 \(f(p)\) 可以砍掉这个 \(\Sigma\)?不是很懂。

代码

计数选做

CF1085G Beautiful Matrix

比较自然的题。

首先发现总方案数就是 \(n!\times f^{n-1}_n\),其中 \(f_n\) 是错排数。也就是第一行任意确定一个排列,接下来每一行都是个错排。然后我们相当于给了一个方案,问多少个方案的字典序比它小。

第一行就不同的情况是平凡的。然后我们接下来认为第一行的方案和给出的方案一致。

如果我们枚举第一次不同的行,那就是在做 \(n\) 次下列的问题:

  • 给出排列 \(p\) 和它的错排 \(q\),问有多少个 \(p\) 的错排 \(q'\) 字典序比 \(q\) 小。

朴素的想法是:枚举 LCP 长度 \(i\)\(q'_{i+1}\),此时的方案数相当于是一个局部错排问题:也就是长度为 \(a+b\) 的排列,对于 \(i\le a\) 要求 \(p_i\neq i\),对于 \(i\gt b\) 问要求,询问 \(p\) 的数量。记这个数目为 \(F(a,b)\)

先考虑 \(F(a,b)\) 的转移:类似原始的错排问题的考虑方式,先特判平凡的 \(F(a,0)\)\(F(0,b)\)。然后我们考虑分类讨论 \(p_1\le a\)\(p_1\gt b\) 即可。于是可以 \(O(n^2)\) 地预处理出 \(F\)

然后我们得到了一个 \(O(n^3)\) 的做法,显然可以进一步优化:因为我们枚举 \(q_{i+1}'\) 的时候,其实仅关注其是否在 \(a\)\(p_i\neq i\) 的约束位置里,所以如果我们能计算出这样的 \(q_{i+1}'\) 的数目就结束了。而显然也可以在枚举 LCP 的过程中用树状数组维护出来。这样就在 \(O(n^2\log n)\) 的时间内解决了本题。

代码

分层图

找不到出处。

一个 \(m\)\(n\) 列的矩阵。满足:

同一行的任意两点间有连边。

同一列的相邻两点间有连边。

求:这张图的哈密顿回路数量。\(\mod 10^9+7\)

\(n\le 500,m\le 10^4\)

我们考虑暴力一些的做法:直接从上到下扫描一个哈密顿回路。可以发现我们考虑完了前 \(i\lt n\) 行以后,观察那些第 \(i\) 行和第 \(i+1\) 行的连边,有一些是 \(i\rightarrow i+1\) 的,有一些是 \(i+1\rightarrow i\) 的,两者数目应该相同且非零。于是我们可以设 \(f(i,x)\) 表示考虑完了 \(\le i\) 的行,此时留下 \(x\) 个插头的方案数。

计算出 \(f(m-1,x)\) 后,容易计算最后一行对应的方案数并且计算答案。

\(f(1,x)\) 也是容易计算的。然后我们考虑 \(f(i,x)\rightarrow f(i+1,y)\) 的转移。显然系数和 \(i\) 无关,因此我们可以转而用矩阵快速幂去维护,做到 \(n^3\log m\) 的复杂度。现在就是要确定转移的系数。

但其实这里才是最复杂的地方:我们称第 \(i\) 行伸出来的 \(x\) 个插头是红色,第 \(i+1\)行伸出来的 \(y\) 个插头是蓝色。你考虑每个蓝色插头都应该位于一对红色插头的内部(一对插头是,先从 \(i\) 走到 \(i+1\),再从 \(i+1\) 走到 \(i\) 的)。

而且注意到一对蓝色插头和一对红色插头还能重合,还可以是一端重合,另一端不重合。 这就非常麻烦。。

考虑可以枚举一个 \(k\):表示恰好有 \(k\) 对红色插头内没有接任何蓝色插头。则我们至少要给这 \(k\) 个空分配 \(2\) 个数(对应了插头的两端)。然后我们把 \(j\) 个蓝色插头分配给 \(i-k\) 个红色插头。此时可以看成:先给每个蓝色插头分配 \(2\) 个数,此时还剩下 \(n-2(j+k)\) 个数,分配给共 \(i+j\) 个空即可。比如:假如有 \(2\) 个红色插头,\(3\) 个蓝色插头。并且我们把前两个蓝色插头给到了第一个红色插头的组里。那么就是:第一个红色插头那边有 \(3\) 个空,第二个那边有 \(2\) 个空。如果第一个红色插头的左侧那个空没分配数,就说明红色插头的左侧,和蓝色插头的左侧是同一个数,右侧同理。这样就可以不重不漏地考虑到所有情况。

最后在 \(f(1,x)\) 的初始化和最后 \(f(m-1,x)\rightarrow ans\) 的部分有一些细节系数需要注意。不再赘述。

这个题可能还是很需要代码的:代码

2021 集训队互测 Imbalance

写起来让人有点汗流浃背。

对于 \(k\le 22\) 我们暴力状压判掉,然后 \(\frac{n}{k}\) 不会很大。

首先考虑,写成一个 \(\lceil \frac{n}{k} \rceil\) 行,\(k\) 列的矩阵。然后每一个位置填上 \(0/1\)。那你考虑就是:对于每个格子 \((i,j)\),考虑 \((i-1,j+1)\rightarrow (i-1,k)\) 以及 \((i,1)\rightarrow (i,j)\)\(k\) 个格子加起来不能为 \(\frac{k}{2}\)。于是我们先枚举前 \(\lceil \frac{n}{k}\rceil-1\) 行的和,然后再一列一列地 dp,记录每一行当前的和。这样可以通过 \(n\le 66\) 的部分分。

首先我们需要一个转化:注意到如果有两段长度为 \(k\) 的,一个里 \(0\) 的个数 \(\le \frac{k}{2}\),另一个里 \(\ge \frac{k}{2}\),则一定还存在一段 \(=\frac{k}{2}\) 的。所以我们只用算:每一段里 \(0\) 的个数都 \(\lt \frac{k}{2}\) 的,以及每一段里 \(0\) 的个数都 \(\gt \frac{k}{2}\) 的即可。显然这两个是本质相同的问题。

\(r=\lceil \frac{n}{k} \rceil\),则其上界为 \(5\)。然后,我们考虑把前缀和 \(s_i\) 写在 \(i\bmod k\) 的位置,这样我们就有 \(r\) 条折线。

我们的约束实质上是考虑编号相邻的两条折线:然后任意选中一个 \(x\),然后他们在 \(x\) 处的点值差应该小于 \(k'=\frac{k}{2}\)。然后我们不妨把第 \(i\) 条折线整体向下移动 \((i-1)k'\),这样约束就变成了第 \(i\) 条直线严格在第 \(i-1\) 条直线的下方。我们 \((k')^{r}\) 地枚举每一行的前缀和,然后就确定了 \(r\) 条路径的起点与终点,做不相交路径计数。

\(S\) 的给出实际上限制了第一条路径的前 \(m\) 步,不妨先来考虑 \(m=0\) 的情况。还有一个细节是最后一行因为有残缺所以终点并不位于 \(x=k\) 处。这实质上带来一个问题:就是当所有终点都在 \(x=k\) 处的时候,想让路径不相交就只能让第 \(i\) 个起点和第 \(i\) 个终点匹配。但是此时我们就可以违反这个事情:比如假设有两行,那可以第一个起点和第二个终点匹配,第二个起点从下面绕上去和第一个终点匹配。因此我们需要强制 ban 掉最后一个终点右下方的所有点。然后路径数就不方便用组合数统计了,需要跑一个简单的 dp。

\(m\gt 0\) 就是第一条路径的起点也被钦定,我们故技重施:把这个起点左上方的点也 ban 掉即可。

复杂度的话,大概是 \(O((k')^{r}\times r^3)\),还有一个 dp 的复杂度。我们需要提前最外层枚举最后一行的答案然后跑出 dp 后再开始 dfs,这里一个比较松的分析是 \(O(n^4)\) 的,足够通过了。

代码

2021 集训队互测 愚蠢的在线法官

很有启发性的一个题!

本题有非常多的理解角度,但我认为官方题解给出的角度很有启发性,我也主要从它的角度来写一下理解。

一些比较平凡的事情:若 \(A_i\) 有重复元素则不满秩因此行列式为 \(0\);交换两个 \(A_i,A_j\),因为同时交换了一行一列所以行列式不变。因此我们不妨认为 \(A_i\) 按照 dfs 序给出。

注意到一个看上去非平凡的部分分是 \(A_i=i\)。因此来考虑研究此时的情形。

可以手玩出:菊花的答案是 \(v_1\prod_{i\ge 2}(v_i-v_{1})\),链的答案是 \(v_1\prod_{i\ge 2}(v_i-v_{i-1})\),加以对于一些小的树的手玩可以发现规律:令 \(w_i:=v_{i}-v_{fa_i}\),则答案就是 \(\prod w_i\)

一个形式化的证明也非常精彩:

\(i\mid j\) 表示 \(i\)\(j\) 的祖先(可以有 \(i=j\))。然后 \(v_{LCA(i,j)}\) 可以写成:\(\sum_{k}[k\mid i][k\mid j]w_k\)。这显然是一个矩阵乘法的形式:\(A(i,j)=\sum_{k}B(i,k)B(k,j)\)。因此,构造矩阵 \(B,C\)

\[B_{i,j}=[j\mid i]w_k \\ C_{i,j}=[i\mid j] \]

然后,就有 \(A=B\times C\)。对于方阵而言,众所周知 \(|A|=|B|\times |C|\)

显然 \(|B|\)\(|C|\) 都是下三角矩阵,有:\(|B|=\prod_{i}w_i,|C|=1\),因此就有 \(|A|=\prod_{i=1}^{w_i}\)

有趣的是,这个证明可以非常容易地来做 $\gcd $ 矩阵的行列式,也就是 \(|A_{i,j}=v_{gcd\{i,j\}}|\)。这里暂且不走远

对于 \(k\lt n\) 的一般情况,考虑依然可以仿照上面的形式:构造出 \(k\times n\)\(n\times k\) 的矩阵 \(B,C\) 相乘:

\[B_{i,j}=[j\mid A_i]w_j\\ C_{i,j}=[i\mid A_j] \]

依然有 \(A=B\times C\),但由于 \(B,C\) 不是方阵,我们无法用上面的方式计算行列式。

Cauthy-Binet 公式给出了此时的一个拓展:

\[|A|=\sum_{S\subseteq\{1,2,...,n\},|S|=k}|B_{\{1,2,...,k\},S}|\times |C_{S,\{1,2,...,k\}}| \]

考虑其组合意义:我们称 \(a_1,a_2,...,a_k\) 为左部点,然后我们相当于又选中了一个大小为 \(k\) 的点集,称为右部点(两个集合可以有交)。然后我们考虑所有左部点和右部点的匹配方案(一个左部点 \(u\) 能匹配到右部点是 \(v\) 当且仅当 \(v\mid u\))。显然每个匹配方案都对应了一个排列 \(\sigma\),令 \(|\sigma|\) 是其逆序对数,然后一个匹配方案的权值是 \((-1)^{|\sigma|}\) 乘上右部点的 \(w\) 之乘积。

考虑我们可以设 \(dp(u,x)\) 表示 \(u\) 子树内还有 \(x\) 个左部点的答案和。但是这样一来复杂度爆炸二来干不掉逆序对的影响。一个事情是如果 \(x\ge 2\) 那么你发现,子树内两个左部点,和往上的两个右部点都能匹配,你交换一下权值不变,但是逆序对奇偶性改变。然后可以感知到这个是能够抵消掉的。所以我们强制要求选出的右部点只能有一种匹配方式。

那就设 \(dp(u,0/1)\) 就好了,转移的时候根据 \(u\) 是否为左部点进行讨论,时间复杂度 \(O(n)\)

代码

zjk 大神也有一个深刻的理解方式,有空学习了补充一下。

2021 集训队互测 抽奖

神题。

首先需要知道 K-FWT 的存在:一个常用的做法是,考虑原本的卷积的实质:依次考虑每个维度,然后在枚举其他位的构成,此时有一个长度为 \(k\) 的数组,我们在这一位上做长度为 \(k\) 的循环卷积即可。

而长度为 \(k\) 的循环卷积可以用 DFT 来实现:事实上 DFT 的过程就是在做循环卷积,不过平常我们会让 \(n\) 足够大使得循环卷积变成了线性卷积。我们这里的 DFT 并不需要用到 FFT 那种神秘的分治处理,直接暴力 \(O(k^2)\) 地得到 \(1,\omega_{k}^{1},\omega_{k}^{2},...,\omega_{k}^{k-1}\) 处的点值,将其替换掉原本的数组即可。

这样,我们要做枚举 \(n\) 个数位,每次需要 \(k^{n-1}\) 地枚举其它数位的表示,然后 \(k^2\) 地暴力 DFT。因此时间复杂度是 \(O(nk^{n+1})\) 的。

上面的都是基础的 K-FWT 内容。原始的 FWT 存在一个 \(f(S)\rightarrow f'(T)\) 的形式化表达,我们这里也存在一个,从原数组 \(f\) 到 FWT 结果 \(f'\) 的形式化表达:

\[f(S)\rightarrow f'(T) : \prod_{i=1}^{n}\omega_{k}^{S_iT_i} \]

换言之,\(f\) 数组的第 \(S\) 项,给 \(f'\) 数组的第 \(T\) 项的贡献系数是一个 \(k\) 次单位根,这个单位根的上指标就是 \(\sum_{i=1}^{n}S_iT_i\),也就是 \(S\cdot T\)(这里把 \(S,T\) 看成了一个 \(n\) 维向量)。

然后考虑这一题的做法:我们直接用长度为 \(n\) 的三进制数来表示整个状态。令 \(c_1(S)\)\(S\) 的三进制表示里 \(1\) 的个数,类似定义 \(c_2(S)\)

那么,从 \(f(i,S)\) 转移到 \(f(i+1,T)\) 的系数是多少?令 \(X:=T\ominus S\)\(\ominus\) 定义为不退位减法),则显然方案数就是 \(\sum_{i=1}^{m}[c_1(x)=a_i\land c_2(x)=b_i]\)

\(f(S) := \sum_{i=1}^{m}[c_1(S)=a_i\land c_2(x)\land b_i]\),然后定义乘法是三进制异或卷积。则我们要求的就是 \(f^k\)\(O(n^2)\) 条信息:对于每个 \(i+j\le n\),去计算所有满足 \(c_1(S)=i\land c_2(S)=j\)\(f^k(S)\) 的和。

直接 \(n3^{n+1}\) 地去暴力做 3-FWT 显然是不可行的,我们考虑这样一个事情:就是如果 \(c_1(x)=c_1(y)\land c_2(x)=c_2(y)\),则非常显然有 \(f(x)=f(y)\)

于是我们可以用 \(O(n^2)\) 个二元组 \((i,j)\) 来描述整个 \(f\) 的信息:对于一对 \((i,j)\),我们令 \(S\) 是前 \(i\) 位都是 \(1\),接下来的 \(j\) 位都是 \(2\),其余位都是 \(0\)。然后令 \(f(i,j) := f(S)\)(你也可以任选另外一个你喜欢的 \(S\),但是这样选比较方便)。显然我们要求的就是所有 \(f^k(i,j)\times \dbinom{n}{i,j,i-j-j}\) 的信息。把这个数 \(S\) 称作 \(key(i,j)\)

并且,根据上面 \(f\rightarrow f'\) 的表达方式,可以发现我们也可以用同样的方式,用 \(O(n^2)\) 个二元组 \((i,j)\) 来描述整个 \(f'\) 的信息。那么我们只需要完成 \(f\rightarrow f'\) 的步骤,然后直接令 \(f'\) 的每个元素 \(f'(i,j):=f'(i,j)^{k}\),最后再 IFWT 回去即可。压力就来到了 \(f\rightarrow f'\) 的步骤。

最暴力的想法是:直接枚举一个 \(f(i,j)\) 给到 \(f'(x,y)\) 的贡献。令 \(T := key(i,j),S := key(x,y)\)。 也就是暴力枚举 \(T\),然后考虑 \(f(T)\rightarrow f'(S)\) 的贡献。

此时,直接逐位考虑 \(T\) 的取值,并且状态中记录当前的 \(i=c_1(T)\)\(j=c_2(T)\),然后维护此时的 \(\sum \omega^{S\cdot T}\) 即可。这实际上是一个背包的过程,将其记作 \(G(i,j)\)。然后最后 \(G(i,j)\times f(i,j)\) 贡献到 \(f'(S)\) 上即可。你发现这个过程中,我们已经考虑掉了所有和 \(T\) 属于同一等价类的数给 \(S\) 的贡献。换言之这个 dp 其实是算出了“一个等价类”去给一个确定的“数”的贡献。

然后这就是一个 \(O(n^5)\) 的做法。瓶颈在于对于每一对 \((x,y)\) 我们需要 \(O(n^3)\) 的背包去得到共 \(O(n^2)\)\(G_{x,y}(i,j)\) 的信息。考虑 \(G_{x,y}\) 其实可以看成:\(x\)\(1\) 类物品,\(y\)\(2\) 类物品,\(n-x-y\)\(3\) 类物品卷积(背包)的结果。因此可以从 \(G_{x,y-1}\) 出发,\(O(n^2)\) 地推得 \(G_{x,y}\) 的信息:我们撤销掉一个 \(3\) 类物品,再加入一个 \(2\) 类物品即可。类似地也能够从 \(G_{x-1,y}\) 出发,\(O(n^2)\) 地推得 \(G_{x,y}\) 的信息。总时间复杂度是 \(O(n^4)\) 的,需要注意常数的效率。

代码

300iq Contests 3 H. Horrible Cycles

两年前对着想了一个晚上没做出来,现在秒了,很有进步!

将二分图的两部成为黑白两部,然后题意即为第 \(i\) 个白点向前 \(a_i\) 个黑点有连边。

一个环可以看作一个圆排列:由于是二分图,我们知道这个圆排列上的点一定是黑白交替的。

我们为了避免重复计数:强制以排列中编号最小的黑点作为排列开头。并且认为 \(p_{2k+1}=p_{1}\),这里环长即为 \(2k\)

这个排列需要满足什么性质?

  • 对于排列中的一个白点 \(u\),假设两侧的黑点编号为 \(x,y\),则 \(a_u\ge \max\{x,y\}\)

这启发我们:把黑白点混合在一起排序,黑点的第一关键字为编号,白点的第一关键字为 \(a\);第二关键字是点的颜色,黑点优先。这样,上面的条件就转化成:排序后,白点的 \(rk\) 应该大于两侧黑点的 \(rk\)

考虑排序后逐个考虑每个点是否加入我们的排列,并且实时维护我们当前得到的排列 \(P\)

\(dp(i,j)\) 表示考虑完前 \(i\) 个点,且当前的排列 \(P\) 拥有 \(j\) 个连续段的答案。这是常用的计数套路。

容易归纳发现每个连续段都是最外层为黑点的,与下面的转移很好地形成结合:

当加入白点的时候,我们一定是放在两个连续段中间并合并,也就是 \(dp(i,j)\times (j-1)\rightarrow dp(i+1,j-1)\)

当加入黑点的时候,我们一定是放在两个连续段中间,并且这里形成了两个空等待后续去填,也就是 \(dp(i,j)\times (j-1)\rightarrow dp(i+1,j+1)\)。这里的系数是 \(j-1\) 而非 \(j+1\),因为我们无法在最两边的连续段外侧插入这个黑点(我们已经强制了 \(p_1\)\(p_{2k+1}\) 都是第一个黑点,也就是整个排列的最小值)。

然后 \(O(n^2)\) 做完了。

代码

CF1264D Beautiful Bracket Sequence (hard version)

为啥要写这个,给下一道题铺垫一下。

先考虑一个确定串的权值:若出现 )( 的形式,则不妨同时删去之。因此易得答案一定形如 ((...)) 的形式。

再来考虑计算:枚举位置 \(i\),使得位置 \(i\) 上是 ( 且被统计进答案,则其实质上要求:

  • \(i\) 左侧的 ( 数少于 \(i\) 右侧的 ) 数。

假设其左侧有 \(a\) 个确定的 (,右侧有 \(b\) 个确定的 ),两侧分别有 \(c_0,c_1\)?,我们枚举左侧取 ( 的问号数量 \(p\),以及右侧取 ) 的问号数量 \(q\),即得:

\[\sum_{a+p\lt b+q}\dbinom{c_0}{p}\times\dbinom{c_1}{q} \]

限制也即:\(q-p\gt a-b\),枚举 \(d=q-p\gt a-b\),即得:

\[\sum_{d\gt a-b}\sum_{q-p=d}\dbinom{c_0}{p}\dbinom{c_1}{q} \]

内层根据范德蒙德卷积得:

\[\sum_{d\gt a-b}\dbinom{c_0+c_1}{c_1-d} \]

因为 \(c_0+c_1\) 只有 \(2\) 种取值,现在我们要做的就是预处理出 \(\dbinom{c_0+c_i}{x}\) 关于 \(x\) 的前缀和,整个问题在 \(O(n)\) 时间内解决。

代码

CF1450H2 Multithreading (Hard Version)

这个题对数学能力,代码能力,细节的处理都有很高的要求。说实话有点恶心了。

先研究一个固定串的答案:每次消去两个同色相邻点,然后两边的合并起来,这个过程显然正确。

最后剩下的黑白点颜色应该相同,且都为偶数。设剩下 \(x\) 个黑色点则 \(\frac{x}{2}\) 即为答案。

对于一个 \(0/1\) 序列,如何判断其能通过“消去两个同色相邻点”的操作消除完毕?这是 CSP2023T2 的一个弱化,但是在字符集为 \(2\) 的时候有非常好的性质:充要条件即为,对于两种颜色,其在奇数位置上的出现次数等于其在偶数位置上的出现次数。

基于此,设有 \(x\) 个黑色点位于奇数位置,\(y\) 个黑色点位于偶数位置。则容易发现答案即为 \(|x-y|\)(显然基于白点算出来应该是同样的结果)。

这样,对于一次询问,也即 H1,我们便容易得到答案。

设黑色点在奇数/偶数分别有 \(a_{0/1}\) 次出现,然后问号在奇数/偶数,分别有 \(c_{0/1}\) 次出现。枚举奇数位置的问号变成黑色的数量 \(p\),以及偶数位置的数目 \(q\),易得:

\[\sum_{2\mid (a_0+a_1+p+q)}|(a_0+p)-(a_1+q)|\dbinom{c_0}{p}\dbinom{c_1}{q} \]

这个形式与上题即为接近:同样考虑转而枚举 \(d=p-q\),则:

\[\sum_{2\mid (a_0+a_1+d)}|a_0-a_1+d|\dbinom{c_0+c_1}{c_0-d} \]

到这里足以通过 H1。在接下来处理前,我们在对式子做一些化简:

考虑枚举 \(d := c_0-d\),也即:

\[\sum_{2\mid(a_0+a_1+c+d)}|a_1-a_0+d-c_0|\dbinom{c_0+c_1}{d} \]

按照 \(d\)\(-(a_1-a_0-c_0)\) 的大小关系分类讨论,拆开绝对值,我们可以归约到带修维护下面的问题:

给出 \(k,n\),对 \(x=0/1\),求:

\[\sum_{d\equiv x\bmod 2,d\le k}\dbinom{n}{d} \]

(有时候最原始求和对象是 \(d\dbinom{n}{d}\),可以利用吸收恒等式转为 \(n\dbinom{n-1}{d-1}\))。

保证 \(k,n\) 时刻非负,且 \(k,n\) 有若干次自增,自减的变化。

对于这样一个问题:我们考虑消去 \(d\equiv x\bmod 2\) 的限制:因为 \(\dbinom{n}{d}=\dbinom{n-1}{d-1}+\dbinom{n-1}{d}\),因此如果可以维护下列的答案,我们就可以在 \(O(1)\) 的时间内修正得到当前问题的答案:

给出 \(k,n\),求:

\[\sum_{d\le k}\dbinom{n}{d} \]

此时,\(k\) 的自增,自减,是容易维护答案的变化的。

\(n\) 自增的时候,利用 \(\dbinom{n+1}{d}=\dbinom{n}{d-1}+\dbinom{n}{d}\),也容易 \(O(1)\) 维护答案的变化。自减是逆过程,同理可维护。

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

实现的时候建议多写几个部分的暴力,一点一点改。

代码

洛谷P7228 纯洁憧憬

以析合树的角度理解会非常容易。

首先,对于一个排列:极长非平凡连续段在根节点研究就足够了:

对于析点:其应该有 \(\ge 4\) 个儿子:且不存在跨越两个儿子间的连续段,那么答案就应该是长度最大的儿子。

相当于用若干个数拼出 \(n\)(且你使用 \(x\) 这个数就有 \(x!\) 的系数贡献),且至少有一个数 \(\gt k\),容易 dp。

儿子之间的拼接也有顺序:这导出另一个问题:长度为 \(cnt\) 的排列,不存在长度 \(\gt 1\) 的非平凡连续段的数量。其实就是 \(k=1\) 的情况,这个问题留在后面解决。

对于合点:其应该有 \(\ge 2\) 个儿子,且有递增/递减两种等价排列顺序。仅以递增为例:

则最大的非平凡连续段应该是 \(n\) 减去第一个 or 最后一个儿子的长度。因此还是相当于若干个数拼成 \(n\),然后第一个儿子 or 最后一个儿子要至少有一者 \(\le n-k\),容易 dp 求出。

这一次儿子之间的拼接只有递增/递减两种顺序,不需要额外的考虑了。

再来考虑 \(k=1\) 的情况:我们记此时的答案为 \(f(n)\)

依然从析合树的角度来考虑,这次考虑从容斥的角度考虑,也就是去掉不合法区间。那么分析析点的时候,大小为 \(n\) 的树至多应该有 \(n-1\) 个儿子,就不会出现用到 \(f(n)\) 的情况了。然后可以 \(O(n^3)\) 地完成递推。

代码

CTSC2018 青蕈领主

容易联想到析合树的结构,但并不需要。

首先给出的区间不应有相交而不包含的关系,因为这样会得到一个更大的连续段。此时考虑按照包含关系建树。

根节点应为 \([1,n]\),然后对于一个表示 \([l,r]\) 的区间而言,它的儿子区间应该恰好拼成 \([l,r-1]\) 全体。

接下来仅考虑儿子节点之间的排布顺序即可,此时变成这样一个问题。

长度为 \(cnt+1\) 的排列,使得所有非平凡连续段都包含 \(p_{cnt+1}\),记为 \(f(cnt)\)。显然 \(f(0)=1,f(1)=2\)

转置,变为对逆排列计数,使得所有非平凡连续段都包含最大值。

为什么想到转置?即使我们不转置,直接推导,最后也会用到转置后的问题答案。然后我们会意识到两者是一个问题,不如预先就转置好。

考虑增量地计数,也就是研究 \(1\) 的位置,构建递推式:

  • 若去掉 \(1\) 后,剩下的排列是合法的。则考虑在一个合法的排列中插入 \(1\)。显然我们不插入在 \(2\) 旁边即可(这一点仅在 \(n\ge 2\) 时成立),因此 \(f(n)\)\(f(n-1)\times (n-1)\) 贡献到。
  • 若去掉 \(1\)后,剩下的排列是不合法的。考虑所有不包含 \(n+1\) 的非平凡连续段:应该只有一个极长的这样的连续段。枚举其长度 \(j\in [2,n-2]\)。然后有两个问题:在这个段里插入 \(1\) 的方案数,以及把其看成一个整体后,\(i-j\) 个元素的排布方式。显然两者答案分别为 \(f(j)\times f(i-j)\),然后又因为我们有 \(n-j-1\) 种选取这个连续段的值域的方式,所以 \(f(n)\)\(f(j)\times f(i-j)\times (n-1)\) 贡献到。

如此我们在 \(O(n^2+Tn)\) 的时间内完成了本题。

\(f\) 的计算是全在线卷积的形式,可以 \(O(n\log^2 n)\) 计算之。其实我本来不会这个的,α 指点了一下:

我们倍增求解,然后已知 \(f[0,n]\) 求解 \(f(n,2n]\) 的时候是半在线卷积。这是一个 \(T(n)=T(\frac{n}{2})+O(n\log^2n)=O(n\log^2n)\) 的复杂度。

代码

CF1603F October 18, 2017

有点 ez 哦。

考虑枚举线性基的秩 \(r\)

  • 我们假设基底是 \(x_1,x_2,...,x_r\)。则考虑基底的选取方式。

对于 \(x_1\),它不能选取 \(0\) 也不能选取 \(X\),所以我们发现我们需要讨论 \(X=0\)\(X\neq 0\) 的两种情况。先考虑比较一般的 \(X\neq 0\)。此时 \(x_1\) 就有 \(2^m-2\) 种选法。对于 \(x_2\),它不能选取 \(0,X,X\oplus x_1,x_1\) 四个数,就有 \(2^{m}-4\) 种选法。更一般地,\(x_i\)\((2^{m}-2^{i})\) 种选法。

对于不在线性基的 \(n-r\) 个数,若其位于 \(x_i\)\(x_{i+1}\) 之间,则有 \(2^{i}\) 种方案数。

这实际上对应了 q-binom 的组合意义:总答案就是 \(\binom{n}{r}_{2}\)

对于 \(X=0\),那么没有非空子集异或和为 \(0\) 就等价于 \(a\) 的线性基就是 \(a\) 全体。这就是 \(\prod_{i=1}^{n}(2^m-2^{i-1})\)

综上本题在 \(O(\min\{n,m\})\) 的时间内解决。

代码

ARC139F Many Xor Optimization Problems

有点 hard 哦。do_while_true 的题解写的非常清晰,我来复读一手。

怎么描述异或的最大值?

假设线性基的主元位是 \(0\le a_1\lt a_2\lt ... \lt a_{k}\lt m\)。则 \(f(A)\)\(2^{a_i}\) 这些位上必定为 \(1\),而其余 \(\lt a_k\) 的位有可能为 \(0\) 也有可能为 \(1\)。但显然,在所有主元位是 \(S=(a_1,a_2,...,a_k)\) 的情形里,某个 \(2^{j}\)\(j\lt a_k\) 且不和任意 \(a_i\) 相等)恰有 \(\frac{1}{2}\) 的概率为 \(1\)。因此我们容易算出此时 \(f(A)\) 的期望。一个思路是:再算出方案数,然后相乘贡献到答案里即可。所以期望是啥呢?显然为 \(\frac{1}{2}(2^{a_k+1}-1+\sum_{j\in S}2^{j})\) 。那么方案数呢?这里其实比较复杂,因为可能原始的序列里,线性基的最高位并不为 \(\{a_1,a_2,...,a_k\}\),换言之我们枚举的已经是线性基经过一些处理后的结果,那么就不太能考虑原序列里每个数的取值)。正确的姿势是转而考虑每个位:

  • 对于非主元位而言,只有线性基中的 \(k\) 个数允许在这些位为 \(1\),否则意味着多出了主元。那么线性基里对应主元是 \(2^{a_i}\) 的人,它的非主元位置就有 \(a_i-(i-1)\) 个,所以这部分的方案数是 \(\prod_{i=1}^{k}2^{a_i-(i-1)}\)
  • 对于主元位而言,即使是非线性基内的元素也允许有取值:因此我们相当于是有 \(n\)\(k\) 维向量,要求秩为 \(k\)。由于行秩等于列秩,转置一下变为 \(k\)\(n\) 维向量,要求秩还为 \(k\),这就好做多了,因为每个向量都被约束到了:第 \(i\) 个向量有 \(2^{n}-2^{i-1}\) 种方式,所以这部分的方案数是 \(\prod_{i=1}^{k}(2^{n}-2^{i-1})\)

接下来大家可以把这些东西都乘一下,我就不乘了。

我们枚举秩 \(k\),然后上面有很多乘数都只和 \(k\) 有关,可以提取出来。只有期望,还有 \(\prod_{i=1}^{k}2^{a_i-(i-1)}\) 这两兄弟很麻烦。对于后者,我们把 \(2^{-(i-1)}\) 提取出来,也就是提一个 \(2^{-\binom{k}{2} }\),然后内部变成 \(\prod_{i=1}^{k}2^{a_i}\)

接下来把期望的三项内容分别拆出来计算。\(-1\)\(2^{a_k+1}\)\(\sum_{j\in S}2^{j}\),都要与 \(\prod_{i=1}^{k}2^{a_i}\) 相乘后求和。好像难度递增,而又并不存在谁严格比谁更强的关系,那就逐个考虑了。。

\(-1\):那其实只需要求 \(\prod_{i=1}^{k}2^{a_i}\) 的和,这不是我们 q-二项式定理吗?答案就是 \(2^{\binom{k}{2}}\binom{m}{k}_2\)

\(2^{a_k+1}\):那我们先枚举一下 \(p=a_k\),那就是 \(2^{2p+1}\times 2^{\binom{k-1}{2}}\binom{p}{k}_2\)。这咋复杂度平方了?别急,等会化简。

\(\sum_{j\in S}2^j\):这个看着其实更难,需要灵机一动一下:看成是 \((2^m-1)\) 减去所有 \(j\notin S\)\(2^j\)。然后就相当于是你选 \(k+1\) 个位置,再钦定一个位置是 \(j\notin S\) 的那个,那 \((2^m-1)\) 是容易的,后面就是 \((k+1)2^{\binom{k+1}{2}}\binom{m}{k+1}_2\) 了,这个化出来咋比第二个还容易?

ok,ok。那第二个咋化简?首先把 \(2^{\binom{k-1}{2}}\) 提出去哦,然后你注意到这个形式和 q-binom 的上指标求和有点像的:

\[\sum_{p=k}^{m}q^{p-k}\dbinom{p}{k}_q=\dbinom{m+1}{k+1}_q \]

考虑 q-binom 的组合意义显然。

\(2^{2p+1}\) 这里,如果只有一个 \(p\),那么我们提个 \(2^{-k}\) 之类的东西就能化成上指标求和的形式。但是有两个 \(p\) 哦。

容易想到把这种东西拆成两个 \(2^{p+...}\) 相乘,但这样我们又无法套上指标求和了哦。

好在我们还有吸收恒等式:

\[\frac{q^{n+1}-1}{q^{k+1}-1}\dbinom{n}{k}_q = \dbinom{n+1}{k+1}_q \]

所以你可以拆一个 \((2^{p+1}-1)\) 作为因子,这样的话这个因子可以化进组合数里,那你外面的指数上就只有一个 \(p\) 了,爽用上指标求和拿下哦。

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

代码

杂题

正睿24省选十连 Round4 B

题意:

给出一个 \(n\times n\) 的黑白矩阵,求有多少个非空子矩阵满足:可以用若干 \(2\times 2\) 的地毯不重不漏地恰好覆盖所有白色格子。\(n\le 300\)

很妙的子矩阵计数。

先考虑猜点充要条件:比如容易猜按列扫描,每一列的白色连通段长度都为偶数;然后对行扫一遍也满足。然后发现一个很强的 hack 是 \(4\times 4\) 的矩形挖掉 \(4\) 个角。

修一下:本质就是说我们要区分那些,作为 \(2\times 2\) 第一列和第二列白格子。在上面的 hack 里:如果按列扫描,显然第一列的两个格子都是作为 \(2\times 2\) 的第一列,然后 \((2,2)\)\((3,2)\) 就应该作为第二列;然后 \((1,2)\)\((4,2)\) 就会作为第一列。然后这个时候因为长度成奇数就矛盾了。然后就能干掉这个 hack。

我们整理一下:按列扫描,然后扫到一个白色格子的时候:如果他的左侧也是白色格子,且这个格子是第一列,那么当前的格子就应该与之合并,作为第二列。否则,这个白色格子应该和右侧的合并,我们到右侧的时候再去处理即可。如果扫到一个黑色格子,而且其左侧是位于第一列的白色格子,那么就不合法。并且要求:任意列上,那些被要求在第一列的格子,构成的每个连续段长度都是偶数。

我们用 \(0/1\) 来维护扫描过的白色格子,要求是在第一列还是第二列。为 \(1\) 表示其在第一列,为 \(0\) 表示其在第二列。然后上面的内容就等价于:扫到白色格子的时候,如果左侧是白色格子,该白色格子的颜色就是其取反,否则为 \(1\)。然后为 \(1\) 的白色格子右侧必须也是白色格子,且每一列上为 \(1\) 的白色格子的连续段长度都得是偶数。

现在考虑:枚举答案的左边界,从左往右枚举右边界。当确定左右边界后,我们尝试快速计算合法的上下边界 \([l,r]\) 的取值数:显然如果有一行出现不合法(为 \(1\) 的格子右侧是黑格子),那么之后的 \([l,r]\) 都不能包含这一行;并且,右边界的\([l,r]\) 范围内不应有为 \(1\) 的黑格子。也就是相当于有一个“全局ban”和“暂时ban”,限制了这一列的 \([l,r]\) 不能包含的一些行。这是容易处理的。

第三个条件,也就是说,\([l,r]\) 和每一列的为 \(1\) 的连续段都是偶数,这个条件可以用 xor hashing 来快速判定:我们给每一列的,每一个 \(1\) 的连续段赋上一个随机权值 \(w\),然后对于一个 \(i\in [1,n]\),令 \(f_i\) 是所有包含这个点的连续段的 \(w\) 的异或和,则 \([l,r]\) 合法就等价于 \(f_l\sim f_r\) 的异或和为 \(0\)。我们确定左右边界后,直接按照 \(r\) 扫描线,使用哈希表/map来维护,即可做到 \(O(n^3)/O(n^3\log n)\) 的复杂度。

代码

一个双序列划分问题

呃呃,我也不知道真正出处。

题意:

给出长度为 \(n\) 的序列 \(a\)。对于一个序列 \(b\),定义其权值为 \(\sum_{i=1}^{|b|}\max_{j\le i}\{b_j\}\)。试讲 \(a\) 划分成两个子序列 \(s,t\),最小化 \(f(s)+f(t)\) 之和。\(n\le 5\times 10^5,1\le a_i\le 10^9\)

这种双序列划分有比较经典的套路:假设我们考虑了 \(1\sim i\) 的划分,考虑 \(x=\max_{j\le i}b_j\),则总有一序列的当前前缀最大值为 \(x\)。令 \(f(i,j)\) 表示说,另一序列的当前前缀最大值是 \(j\) 的答案。考虑转移,令 \(f:=f(i),g:=f(i+1),y=b_i\)。然后我们用 \(f(i)+b\rightarrow g(j)\) 来描述 \(g(j):=\min\{g(j),f(i)+b\}\) 的转移。

讨论 \(x\)\(y\) 的大小关系,容易得到转移:

  • \(x\le y\) 时。

这种情况比较平凡,因为不管 \(y\) 加到哪里都有贡献 \(y\)。有:

  1. 加入 \(x\) 所在的序列:则 \(\forall i,f(i)+y\rightarrow g(i)\)
  2. 加入 \(i\) 所在的序列,则 \(\forall i,f(i)+y\rightarrow g(x)\)
  • \(x\gt y\) 时。

这种情况下会在转移系数有一些额外的讨论:

  1. 加入 \(x\) 所在的序列,则 \(\forall i,f(i)+x\rightarrow g(i)\)
  2. 加入 \(i\) 所在的序列,则 \(\forall i,f(i)+\max\{x,a_i\}\rightarrow g(\max\{i,a_i\})\)

由于一定有 \(i\) 是某个 \(a_j\)(或 \(0\)),因此我们直接离散化即可,这样 \(f\) 的大小就只有 \(O(n)\)。然后暴力实现上述转移容易做到 \(O(n^2)\)。考虑使用数据结构优化上述转移。

\(x\le y\) 的是:我们实质上只需要支持对 \(f\) 数组的三种操作:

  • 全局加常数,单点取 \(\min\),求前缀最小值。

这是很平凡的。而 \(x\gt y\) 的时候我们需要更精细的研究这个过程:

首先我们需要算出 \(f(\lt y)\) 的前缀最小值,用以单点更新 \(g(y)\)。然后,考虑应该是:对于 \(i\lt y\)\(i\gt x\) 而言,我们做的是 \(f(i):=f(i)+x\),对于 \(i\in [y,x]\) 而言,我们做的是 \(f(i):=f(i)+i\)。因此我们的查询还是前缀最小值,但是新增了两个修改:

  • 区间加常数,区间加下标。

到此时有广为人知的分块 + 维护凸包的做法,复杂度显然为 \(O(n\sqrt n)\);也有广为人知的 KTT 做法,复杂度应该是 \(O(n\log^2 n)\)?但是 KTT 超纲,我们来点纲内做法,那么显然要利用这个题里操作的一些限制。

若有两个位置 \(i\lt j\),且满足 \(a_i\le a_j\),则我们说 \(i\) 优于 \(j\),并且排除 \(j\)。维护所有未被排除的位置的集合。则查询前缀 \(\le p\) 的最优解,就是在这个集合里找到最大的 \(\le p\) 的元素,并且查询该位置的值。

注意到我们容易在支持所有上述修改的情况下,\(O(\log n)\) 地返回当前的某个 \(f(i)\) 的值。因此查询单点值的复杂度可以接受,只要可以维护这个集合 \(S\) 即可。

一次区间加常数不会改变这个区间内 \(S\) 的形态;一次区间加下标只会让这个区间内的 \(S\) 缩小(也就是一些位置被前面的人干掉了)。一次单点取 \(\min\) 显然只会最多往 \(S\) 里增加一个元素。 唯一的问题是:考虑我们的 dp 过程,是两侧加 \(x\) 中间加了 \(i\)。因此在 \([1,y)\)\([y,x]\) 这部分里,可能会出现:\([1,y)\) 这里的一个 \(f_i\) 本来能干掉一个 \([y,x]\) 里的 \(f_j\),但是因为这里 \(f_i\) 增加的多,所以导致它干不掉了。也就是 \(S\) 的大小还会进一步变大。

但是理性分析一下:可以发现我们最多只会把位置 \(y\) 加进去,\(\gt y\) 的位置一定不会加进去。因为我们是有一步,把 \(f_{1\sim y-1}\) 的最小值加上 \(y\),和 \(f_y\)\(\min\) 的操作,这个操作很关键:因为本来有个位置 \(i\in [1,y)\) 满足 \(i\) 由于 \(z\) 的话,你注意到 \(f_i+y\) 也一定由于 \(f_z+z\),然后因为你把 \(f_i+y\) 放在了位置 \(y\) 上所以 \(z\) 就肯定能被 \(y\) 干掉。

这样就全对了!然后注意到区间加下标是会让一些 \(S\) 中相邻的元素发生合并。我们需要维护每个位置至少需要多少次区间加下标的操作,才能和后继发生合并,这里需要线段树辅助维护一下。

有一个细节是:因为我们只对 \([y,x]\) 做区间加下标,所以你需要每次重新计算一下 \([1,y)\)\([y,x]\) 还有 \([y,x]\)\((x,\infty)\) 里,最贴近的两个位置的合并时间。

时间复杂度 \(O(n\log n)\),但似乎跑不过 KTT,真菜。

代码

AGC024F Simple Subsequence Problem

有点厉害的题。

考虑没有发现什么厉害的贪心性质,于是尝试直接对每个长度 \(\le n\) 的串 \(x\) 求出其作为哪些集合中的串的子序列出现。由于我们希望一个 \(S\) 中的串只被计数一次,所以考虑子序列自动机那样的判定。

也就是说,一个暴力的做法是:我们直接枚举 \(x\),再枚举集合中的一个串 \(S\),然后用子序列自动机来判定,其实也就是贪心,每次找到 \(S\) 开头的第一个 \(0/1\),然后再把这前面的一段字符删除。

那么我们其实并不关注 \(S\),只关注当前的 \(x\),和删除了一些字符后的 \(S'\)。记录 \(dp(x,S')\) 表示达到这种局面的方案数。则 \(x\) 的出现次数就是 \(dp(x,\emptyset)\),而初始化内容就是对于每个 \(S\),令 \(dp(\emptyset,S)=1\)

转移的时候,直接枚举 \(x\) 的下一位接 \(0/1\),然后在 \(S'\) 中找到其第一次出现即可。

总状态数的话,由于有 \(|x|+|S'|\le n\) 的限制,所以应该是 \(O(n2^n)\) 级别的,因此可以比较容易地做到 \(O(n^22^n)\) 的复杂度。在实现上对空间问题需要一些额外考虑。

代码

后缀自动机期望结点数(by ix35)

题意:

给出一个长度为 \(n\) 的字符串 \(S\),由 0/1/? 三种字符组成。随机给每个 ? 填上 \(0/1\) 的一者,求 \(S\) 的后缀自动机上的期望结点数(包括空节点)。

\(n\le 36\)

首先后缀自动机的节点数有点复杂,比较广为人知的是 \(S\) 的后缀自动机结点数等于反串 \(S'\) 的后缀树节点数(包括根),我们就可以来考虑反转 \(S\) 后计算期望的后缀树节点个数。

由于字符集为 \(0/1\),所以后缀树有一个很好的性质就是,它一定是一颗二叉树。设度数为 \(0/1/2\) 的数量分别为 \(a,b,c\)

我们发现叶子节点很有性质:它一定对应了一个后缀,且这个后缀仅出现一次;对于度数为一的点,我们知道它也一定是对应了某个后缀的出现(不然就会被缩掉),然后因为它的度数为 \(1\),那也就是说这个后缀存在,且其末尾追加 \(0/1\) 得到的两个串中,恰有一者刚好也在 \(S\) 里出现。

而二度节点则是比较一般的节点,没有很好的性质。但是二叉树有一个很涩情的性质:\(c_0=c_2+1\)

所以 \(a+b+c\) 实际上就是 \(2a+b-1\),那么我们就只用来算叶子和一度点的个数了,这两者都是有性质在的。

叶子怎么算?我们定义 \(suf(i)\) 表示 \(S[i...n]\)。然后我们枚举一个 \(suf(i)\),表示说其代表的串作为叶子节点出现,则我们实质上要求:

  • 对于 \(j\lt i\)\(suf(i)\) 都不是 \(suf(j)\) 的前缀。

这个条件可以通过容斥解决:我们有直接暴力 \(2^{i-1}\) 枚举一个 \(\{1,...,i-1\}\) 的子集 \(A\),让所有 \(j\in A\) 的都满足 \(suf(i)\)\(suf(j)\) 的前缀。此时相当于钦定了若干位置间的相等关系,使用并查集辅助维护即可。然后可以在 \(O(n^22^{i})\) 的时间内解决。

数据范围明示了折半,因此对于 \(i\ge \frac{n}{2}\) 我们应该存在另外的指数做法:注意到此时我们可以直接暴力 \(2^{n-i}\) 地确定 \(suf(i)\) 的具体形态,也就是把 \(\ge i\) 的问号都暴力填上。然后再来考虑计数。

此时直接对于 \(j\in [1,i)\) 逐个考虑,设 \(dp(x)\) 是目前 \(A\) 集合里最大的元素,从 \(dp(x)\) 转移到 \(dp(y)\) 显然是容易的。然后我们在 \(O(n^22^{n-i})\) 的时间内也算出了一个 \(suf(i)\) 的答案。

两者平衡一下,就总能在 \(O(n^22^{\frac{n}{2}})\) 的时间内解决叶子的计数问题,设对于 \(suf(i)\) 其作为叶子的概率为 \(p(i)\)

其实一度点的计数也是同理:枚举一个 \(suf(i)\) 然后再枚举后面的一个 \(d=0/1\) 代表追加的一个字符,然后你发现算出来的是啥?好像是整个串不存在 \(suf(i)+d\) 的概率。我们要的是啥?我们要的是存在 \(suf(i)+d\) 且不存在 \(suf(i)+p(d)\) 的概率(其中 \(p(d)\)\(d\) 取反的结果)。

还能补救一下:存在 \(suf(i)+d\) 且不存在 \(suf(i)+p(d)\) 的概率,其实就是不存在 \(suf(i)+p(d)\) 的概率,减去不存在 \(suf(i)\) 的概率(这里都是在考虑 \(suf(1\sim i-1)\) 内)。毕竟不存在 \(suf(i)+p(d)\) 能分成两种情况:第一种是存在 \(suf(i)\) 第二种是不存在。而前者的话,你 \(suf(i)\) 后面跟的不是 \(p(d)\) 就只能是 \(d\) 了。所以字符集为 \(2\) 在这里又发挥了作用。

那么我们如果算出来 \(d=0/1\) 的时候的结果是 \(p_{0/1}(i)\),那么 \(suf(i)\) 作为一度点的概率就是 \(p_{1}(i)-p(i)+ p_0(i)-p(i)\)

我们最后的答案是 \(2a+b-1\),代入以后发现 \(p(i)\) 可以消去,只需算 \(\sum(p_0(i)+p_1(i))-1\)

然后我们在 \(O(n^22^{\frac{n}{2}})\) 的时间内解决了本题。

P.S. 其实还有高论,就是说还有另一个版本:给出字符串长度和字符集大小,对所有串的 SAM 节点数求和。有空再学。

AGC021E Ball Eat Chameleons

想出一个银牌题,兄弟。

以下互换 \(n,k\) 意义,也就是说有 \(n\) 个球,\(k\) 个人。

考虑计数的是:有多少个串可以合法。因此考虑如何判定一个串是否合法。

首先,每一个人拿到的红球数目应该不少于蓝球,并且容易发现:

  • 若红球数目严格多于蓝球,则不管以什么顺序接受都一定合法。
  • 若红球数目等于蓝球,则只需要让最后一次接受的是蓝球即可。

考虑枚举总共的红球个数 \(a\),则有 \(b=n-x\) 个蓝球。

因为每个人的红球数目都不少于蓝球,所以必须有 \(a\ge b\)

因为数目相等的人会比较难合法,所以不会有人出现多拿了两个红球的情况:他完全可以把这个红球分给另外一个红球数目等于蓝球的人。显然只有一个例外,就是所有人的红球都已经被蓝球多了,这对应了 \(a-b\ge k\) 的情况,不难看出一定有解。也就是说当 \(0\le a-b\lt k\) 的时候,令 \(d=a-b\),则应该恰好有 \(d\) 个人的红球比蓝球多一,剩下的 \(k-d\) 个人拿到的红球和蓝球相等。

\(d:=k-d\),则这 \(d\) 个人比较难以分配,注意到它们需要满足一个条件是最后一个人是蓝球。猜测:

  • 只要我们能选出 \(d\) 对球,满足第一个是红,第二个是蓝,且红色的出现在蓝色之前。这就是有解的充要条件。

必要性显然,考虑充分性的话:就考虑剩下的人怎么分配?我们先把 \(k-d\) 个多出来的红色球分给 \(k-d\) 个红球比蓝球多的人。现在,我们剩下的红球和蓝球数目相等。直接全部放给那 \(k-d\) 个人之一即可。

上述讨论的例外是 \(d=k\) 的情况,此时我们也很容易构造出 hack:只要最后一个球是红色就一定非法。但思考后发现加上强制最后一个球是蓝色的额外条件就重新拥有了充分性:因为我们寻找 \(d\) 对红蓝匹配的时候,一定是最前面的 \(d\) 个红色和最后面的 \(d\) 个蓝色匹配。所以最后一个球(蓝色)一定初始属于某个人。我们把剩下的红色、蓝色球全部扔给它,依然不改变其合法性。因此充分性得到证明。

然后考虑计数:实际上就是,第一个红色的出现早于第 \(b-d+1\) 个蓝色的出现,第二个红色的出现早于第 \(b-d+2\) 个蓝色的出现,以此类推。发现这个形式很像卡特兰数的约束:因此考虑转成格路计数:从 \((0,0)\) 走到 \((a,b)\),向右表示红色,向上表示蓝色。然后约束就是不能触碰到 \(y=x+b-d+1\) 这条直线,直接反射容斥即可。

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

代码

AGC022F Checkers

做不动银牌题,兄弟。你们怎么都会?

方便起见,先把一个人的信息看作 \(n\) 维向量 \((v_1,v_2,...,v_n)\)\(i\) 关于 \(j\) 变换就等价于 \(v_{i,x}\) 关于 \(v_{j,x}\) 做对称。然后初始 \(v_{i,j}=[i=j]\)。但这个变换看上去依旧毫无规律。

经过一定手玩,可以发现一件趣味的事情:似乎 \(v_{i,j}\) 的绝对值一定是形如 \(2^{k}\) 的形式。

并且容易理性证明:首先 \(v_{i,x}\)\(v_{j,x}\) 两者,一定有一者是 \(0\)。如果前者是 \(0\),就相当于 \(v_{i,x}:=2v_{j,x}\);如果后者是 \(0\),就相当于 \(v_{i,x}:=-v_{i,x}\)。因此我们永远在做 \(\times 2\) 和取相反数两种变换。

把变换关系建树:若 \(i\) 关于 \(j\) 变换就从 \(j\) 连向 \(i\)。令树根为 \(r\),则最后的 \(v_{r,i}\) 只和节点 \(i\) 在树中的位置信息有关:显然绝对值一定是 \(2^{dep_i}\),而正负性和:\(i\) 的儿子个数,以及所有 \(i\) 的祖先的 \(dfs\) 序在 \(i\) 之后的儿子个数,这两者的总数的奇偶性相关。

第一点启发我们按照深度分层 dp,注意到我们并不关注已经有了多少层。因此可以设 \(dp(x)\) 表示已经填了若干层,且一共有 \(x\) 个节点的方案数。当然我们肯定还要加状态。到这里我们先缓一下。

我们的大体思路是只记录最后一层的若干信息,然后研究相邻层之间发生的贡献。但注意到一件事情是,我们已经填好的层也会收到下一层信息的影响:因为正负性是和每个点的儿子个数奇偶性相关的。

因此考虑令 \(dp(u,x)\) 是我们最后一层里,钦定了恰好 \(x\) 个节点有奇数个儿子。那么初始化应该是 \(dp(1,0)=dp(1,1)=1\),答案应该是 \(dp(n,0)\)。思考转移,考虑枚举下一层的总个数 \(A\),系数是 \(\dbinom{u+A}{A}\),然后呢??

如果我们执着去研究一个 \(v_{r,i}\) 的真实正负性就会去需要额外记录很多东西。但是我们可以转而去记录每个人的正负性相对其父亲是否改变!这样其实变得非常简单:如果上一层的每一个点都有偶数个叶子,那应该就恰好有 \(\frac{A}{2}\) 个人是改变的,恰好 \(\frac{A}{2}\) 个人是不改变的。现在有 \(x\) 个叶子,所以应该有 \(x+\frac{A-x}{2}\) 个人不改变。

我们枚举这 \(A\) 个里有 \(y\) 个人最后算上儿子个数的贡献后会和父亲相同,则有 \(z=A-y\) 个人算上儿子贡献后会和父亲不同。这里有系数 \(\dbinom{A}{y}\)

那么应该恰好有 \(|x+\frac{(y+z)-x}{2}-y|\),也就是 \(\frac{|x-y+z|}{2}\) 个人有奇数儿子。因为他们此时的 \((-1)^{x}\) 和被钦定的 \((-1)^{x}\) 有差异。

这样就是四方:\(dp(u,x)\times \dbinom{u+y+z}{y+z}\times \dbinom{y+z}{y}\rightarrow dp(u+x+y,\frac{|x-y+z|}{2})\),是 \(O(n^4)\),足以通过原题。

考虑可以进一步优化到三方:我们 \(dp(u,x)\) 的转移,先枚举 \(y\) 的值,然后我们就确定了 \((u+y,x-y)\) 的值。所有 \((u+y,x-y)\) 相同的,转移形式也相同。也就是说 \(dp(u,x)\) 枚举 \(y\) 贡献给临时状态 \(g(u+y,x-y)\),然后 \(g(u+y,x-y)\) 再枚举 \(z\) 的大小来转移。这样就是 \(O(n^3)\) 的。需要注意到应该是 \(f/g\) 互相转移,按照第一维的值为顺序来考虑。

代码

正睿24省选十连Round5 B

题意:

给出平面上 \(n\ge 3\) 个点,保证无三点共线,无重点。现在需要选出一个凸包:满足顶点都是给出的点,且内部无给出的点。然后对于凸包外的所有点,向最近的凸包的点连边,形成一个类似花状的结构。令所有连边(包括凸包的周长)长度和为 \(P\),凸包面积为 \(S\),最大化 \(\frac{S}{P}\) 的值。\(n\le 300\)

我的评价是这个题过了以后很大地提升了我在计算几何一方面的水平。他只需要用到极角排序和一些向量运算。

首先最优比率问题容易想到二分答案:现在我们就是要最大化 \(S-\lambda P\) 的一组方案。

这类凸包问题有一类比较通用的方案:考虑枚举凸包上的一点,然后进行 dp。我们这里枚举 \(x\) 最小的点,然后逆时针行走。可以发现,此时我们走过的向量在极角上有顺序:一定是先位于第四象限,再位于第一象限,再位于第二象限,再位于第三象限的。因此我们把向量全部排序,然后按顺序尝试加入每一个向量。

到这里以后我们有比较多的事情要干:为了让凸包内无点,我们需要每加入一条边 \((u,v)\),便与起点 \(s\) 检查 \((s,u,v)\) 构成的三角形是否内部不存在点,此其一也。面积 \(S\) 的计算是容易的:其可以表述为相邻点的叉积和除以二,因此我们只关注上一步走到的点。而 \(P\) 的计算比较难,主要是凸包外到凸包上这一部分,我们只能作每条边的垂直平分线然后给每个点划分出其管辖的区域,这需要我们知道上一条边的信息。所以最后的 dp 里应该记录上一条边的信息。可能还要记录第一条边的信息,复杂度很大。

先把最大的问题:记录边给干掉,这就必须把贡献拆到每个点上。把垂直平分线分成两条,移动到两端点上,然后你发现我们把整个凸包的外侧,划分成了 \(O(m)\) 个矩形半平面和 \(O(m)\) 个扇形半平面(其中 \(m\) 是凸包点数)。而 矩形半平面的贡献是好统计的,因为其只与当前边有关。扇形半平面难统计,因为其与两条边都有关。

注意到扇形半平面内的点,其实是和凸包上当前点 \(u\) 所成极角在一个范围内的点:上下界分别由两条边决定,因此考虑差分。这样我们成功地把贡献分摊给了每条边,那么 dp 就只用记录走到哪个点了。时间复杂度 \(O(n^2)\)。因为我们要二分,还要枚举凸包的最左侧点,所以是 \(O(n^3\log A)\) 的复杂度。

还能做到更优秀:我们令 \(f(i)\) 是以点 \(i\) 为凸包最左侧点能得到的最优答案,则我们先把 \(1\sim n\) 随机打乱得到 \(a_1,a_2,...,a_n\)。然后若 \(f(a_i)\lt f(a_{i+1})\),就不需要跑二分。这样我们只用跑 \(O(f(n)\log A + n)\) 次 dp,其中 \(f(n)\) 为随机的长度为 \(n\) 的排列的单调栈期望点数。比较经典的结论是 \(f(n)=O(\log n)\),那么总复杂度就变为了 \(O(n^3+n^2\log n\log A)\)

还有一个遗留的问题是如何处理三角形内是否有点:我们枚举 \((x,y,z)\) 的一条边 \((x,y)\),再枚举 \(z\) 位于 \(x\) 左侧/右侧。此时按照 \(z\) 关于 \(x\) 的极角扫描所有 \(z\),并且记录已经扫过的 \(z\) 中,向量 \(y\rightarrow z\) 的极角的极值即可。

这样便在 \(O(n^3)\) 的时间内处理完毕。

ARC170E BDFS

被 D 卡了,导致场上没有一点时间来做这个 E。今天补了一下,感觉算比较简单的。

假设我们往 \(1\rightarrow 2\rightarrow 3\rightarrow ...\) 这个方向走了 \(x\) 步,往 \(1\rightarrow n\rightarrow n-1\rightarrow ...\) 这个方向走了 \(y\) 步,那么显然有 \(x+y=n-1\),而贡献应该是 \(\frac{x(x+1)}{2}+\frac{y(y+1)}{2}\)

提出 \(\frac{1}{2}\),则 \(x(x+1)+y(y+1)=x^2+y^2+x+y=(x+y)(x+y+1)-2xy\)。注意到 \(x+y=n-1\),因此化为 \(n(n-1)-2xy\)。因此我们只关注 \(xy\) 的期望值。

\(xy\) 可以化为组合意义:在 \(1\rightarrow 2\rightarrow ...\) 这边的路径上选择一个位置出来,同时在 \(1\rightarrow n\rightarrow n-1...\) 这边的路径上选择一个位置出来,选择方式的方案数。

到这里,做法已经非常明朗:设 \(dp(n,0/1,0/1,0/1)\),表示我们 \(x\)\(y\) 合起来走了 \(n\) 步,前两个 \(0/1\) 分别表示 \(x\) 这边和 \(y\) 这边的路径有无选择出一个位置出来,第三个 \(0/1\) 表示当前的队列顶部是 \(x\) 这边还是 \(y\) 这边。容易做到 \(O(n)\)

使用矩阵快速幂优化转移,容易做到 \(O(\log^3 n)\),矩阵大小为 \(8\),可以轻松通过。

代码

QOJ8130 Yet Another Balanced Coloring Problem

赛场上使用网络流直接通过啊啊,还是太不优美了。其实网络流和正解也很像。

\(R\) 看作 \(-1\),把 \(L\) 看作 \(1\),则问题变成每个节点子树内的叶子和,绝对值不超过 \(1\)

若一个节点子树内部有偶数个叶子,则 \(sum\) 只能为 \(0\),否则必定为 \(1/-1\)。我们称前者为偶点,后者为奇点。则一个点的奇偶性计算仅和其奇儿子个数有关。我们认为叶子也是奇点。

若一个点是奇点,我们将其和它的父亲连边。这样,除了树根和叶子以外,任何点都满足度数是偶数。

树根的奇偶性一定相同,因为题目保证 \(k_L=k_R\)。若两个树根都为奇数,我们在其之间再连一条边。对于叶子:我们直接在左侧的叶子 \(i\) 和右侧的叶子 \(i\) 之间连边。

现在图上所有点的度数都是偶数。用欧拉回路来定向,则每个点的出入度就应该是平衡的。

此时:对每个叶子,考虑两棵树中这个叶子之间的那条边,若从第二棵树指向第一棵树则设为 \(1\) 否则设为 \(-1\) 即可。然后我们便构造出了一组合法解。

那么其正确性何在呢?在第一棵树中,我们可以这样规定:对于一个奇点 \(u\),若 \(u\) 指向 \(fa\) 则代表其子树和为 \(1\),否则其子树和为 \(-1\)。这个 \(u\) 的出入度都相等,我们实质上是约束说:

  • \(cnt\equiv 1\pmod 2\) 个儿子内,出度和入度应该相差不超过 \(1\)
  • 若出度比入度多一,那么就应该是 \(fa\rightarrow u\),否则应该是 \(u\rightarrow fa\)

这两条约束合起来等价于 \(cnt+1\) 条边中恰有一半进,一半出。和欧拉回路的条件是吻合的。

连接两颗树的树根就是为了让所有奇点都有”父亲“,就是有一条额外的连边连出去来表示它的状态。

而连接叶子是为了让两棵树的相同编号的叶子的值相同:我们发现两棵树的视角来看,连接两个叶子之间的边代表的意义相反,所以第二颗树中,\((u,fa)\) 连边的意义应改为:\(fa\rightarrow u\) 等价于和为 \(1\),否则为 \(-1\)

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

代码

AGC018F Two Trees

和上一道题真的非常像。可以互相感受一下。

这个题的要求和上一题略有不同:强制了每颗子树都是 \(1/-1\) 且每个节点都可以有值。

首先注意到子树和的奇偶性只和儿子个数有关:所以一个点在两颗树中的儿子个数奇偶性都应该相同。

同样还是连接两颗树的树根,以此来让所有点都有一个连向”父亲“的边来表述它子树内的值是 \(1\) 或是 \(-1\)

若一个点有奇数个儿子,则算上父亲其度数为偶数,这个时候我们直接让其点权为 \(0\),套用上一题欧拉回路的结论可以发现如果等会我们跑完欧拉回路,那么天然就能保证子树和为 \(1/-1\)

若一个点有偶数个儿子,则其子树和一定是偶数,他就必须选一个奇数点值,考虑还是让他选 \(1/-1\) 就够了?

直接连接两棵树中的这个点的位置,然后你只要规定右指左和左指右分别代表 \(1/-1\) 即可,原理和上一道题一致。

这样同样在 \(O(m)\) 的时间内解决了这个题。实现中我的代码会在有重边的时候出现一些问题,所以将两个点相连的那条边改为了建立虚点 \(0\),然后都去和其连边。

代码

GDKOI2024 计算

首先 \(\gcd(x^{a}-1,x^{b}-1)=x^{\gcd(a,b)}-1\),所以那个界其实就是 \([m^{gcd(a,b)},,m^{gcd(c,d)})\)

发现这个区间有一个很好的性质:每一个 \(\bmod m=i\) 的数出现次数都相同,不妨设为 \(C\)

则变成了这样一个问题:

\(0\sim m-1\) 每一个数都有 \(C\) 个(值相同的可区分),选出若干个数(可空)使得和为 \(m\) 的倍数。

这是一个比较经典的问题。

考虑暴力的想法:由于 \(C\) 很大,我们直接看成是模 \(m\) 意义下的循环卷积。

也就是欲求 \(\prod_{i=0}^{m-1}(1+x^{i})^{C}\) 在模 \(m\) 意义下做循环卷积的常数项。

等价于我们在做长度为 \(m\) 的 DFT 和 IDFT。

DFT 和 IDFT 的本质都是在代入单位根,只不过一个代入的是 \(\omega^{i}\),一个是 \(\omega^{-i}\)。(这里都认为是 \(m\) 次单位根)

那么 \((1+x^{i})\) 做 DFT 后的结果为何?显然其第 \(j\) 项为 \(1+\omega^{ij}\)

那么 DFT 结果相乘后的第 \(j\) 项就应该是 \(\prod_{i=0}^{m-1}(1+\omega^{ij})^{C}\)

求常数项就是代入 \(\omega^{-0}\) 也就是 \(1\),所以我们只需要求:

\[\sum_{j=0}^{m-1}\prod_{i=0}^{m-1}(1+\omega^{ji})^{C} \]

直接用复数表示单位根然后暴力有一个 \(O(m^2)\) 级别的做法,但是大概无法也无法很好的表示?单位根还有更好的性质在。发现 \(C\) 是无关紧要的,我们只要能对每个 \(j\) 求出 \(\prod_{i=0}^{m-1}(1+\omega^{ij})\),再做快速幂就好了。此时来考虑 \(j=1\) 的情况:\(\prod_{i=0}^{m-1}(1+\omega^{i})\) 容易令人联想到 \(x^m-1\) 的展开形式:\(\prod_{i=0}^{m-1}(x-\omega^{i})\)。代入 \(x=-1\) 可以得到 \((-1)^{m}-1 = \prod_{i=0}^{m-1}(-1-\omega^{i})\),也就是原式等于 \([2\nmid m]\times 2\)

对于 \(j\neq 1\) 的情况呢?若 \(\gcd(j,m)=1\) 则你不过是把 \(\omega^{0\sim m-1}\) 置换了一下出现顺序,答案还是不变。对于 \(\gcd(j,m)\gt 1\) 的情况,则实际上就是 \(\frac{m}{\gcd(j,m)}\) 的情况转了 \(\gcd\) 次,快速幂即可。

换言之,答案即为:

\[\sum_{j=0}^{m-1}([2\nmid\frac{m}{\gcd(j,m)}]\times 2)^{C\gcd(j,m)} \]

容易 \(O(m\log m)\) 计算。

更进一步地,由于我们只关注 \(\gcd\),可以暴力枚举约数,预处理 \(\phi\) 后可以单组 \(O(d(m)\log m)\) 计算,可以通过。

代码

LOJ6734 图上的游戏

非常高妙,且很有实现难度的一道交互题。

先考虑确定为链,且链头为 \(0\) 的部分怎么做。

考虑按照 \(1\sim n-1\) 的顺序加入点,增量地维护链上点的顺序结构。并且维护每条边处于哪两个点之间。

初始链上只有点 \(0\),并且所有边都位于 \(0\) 之后。当尝试加入一个点 \(u\) 的时候:我们可以很容易地二分出其位于哪个 \(x_{i},x_{i+1}\) 之间。注意到,我们插入变成 \(x_{i},u,x_{i+1}\) 之后,需要重新确定 \((x_i,x_{i+1})\) 中的连边,此时归属于 \((x_{i},u)\) 还是 \((u,x_{i+1})\) 中。对于一条边可以询问一次求出,但我们的边数可能会很多。因此这个方法最坏会退化到 \(O(n^2)\) 的次数。

考虑将加入点的顺序随机,就在期望 \(O(n\log n)\) 的次数内完成了询问。这样我们完成了链的求解,当然我们是知道链头为 \(0\) 的。

再来考虑树的情况:基本思想是,我们将树剖分成若干条链来求解。那么分为两个步骤:

  • 把树的点集和边集都划分进若干条链。并且用上面的方法确定链内部的结构。
  • 确定链与链之间的连边。

第一步依旧考虑增量地链划分(这里的树剖与通常的不同,会出现一条链的结尾不是叶子的情况):我们按照 \(1\sim n-1\) 的顺序加入点,以 \(0\) 为树根。并且加入到 \(i\) 时,维护出 \(1\sim i\) 到根的链并所形成的结构的链剖分。

当扫描到点 \(u\) 的时候,考虑加入 \(0\rightarrow u\) 的路径。这里有两种情况:

  • \(0\rightarrow u\) 的路径上没有之前未加入过的边。这说明 \(u\) 在已经划分出的某条链上,到底在哪条链上可以直接二分出。

  • \(0\rightarrow u\) 的路径上有未加入过的边,也就是存在一个点 \(v\),使得 \(0\rightarrow v\) 的点均出现过且 \(v\rightarrow u\) 的点均为出现。把 \(v\rightarrow u\) 这一段(不含 \(v\))作为一条新的链。此时已知:这条链的边集,这条链的末端点。而点集会在以后的增量过程中逐渐全部确定。因此链的点,边,端点信息到最后都已知。

我们发现这里对链的描述和之前略有不同:我们是知道距离 \(0\) 最远的那一端的端点,并且知道点集与边集。并且未知的那个端点结尾处还会引出一条边和其它的链相连。但使用原本的思路还是可以确定其形态的。

这里遗留了一个问题:我们如何快速找到 \(0\rightarrow u\) 路径上的边。并且我们的复杂度实质上要求仅与”未加入的边数“有关,而不是”路径边数“有关。

先设计一个和路径边数相关的做法:使用二分法逐一确定路径上的边。具体而言,二分 \(x\),然后禁止掉编号在 \([1,x]\) 的边。若此时 \(0\) 无法到达 \(u\),则说明 \(0\rightarrow u\) 的路径上一定有一条边 \(\le x\)。重复之即可。

再优化成和未加入边数相关的做法:我们只需要在询问的时候强制所有已加入的边都被选中即可。

这样我们在 \(O(n\log n)\) 的步数内完成了链划分及链内部形态的确定。最后一步是确定每条链顶的父亲,注意,我们此时已经知道链顶为 \(x\),也知道与父亲连边的编号 \(id\)。接下来的手法在后续中还有应用。

设计一个序列,使得父亲一定在儿子前出现。然后我们二分父亲的编号 \(mid\):保留前 \(mid\) 个点的导出子图以及 \(id\) 这条边,然后 \(0\)\(x\) 连通等价于父亲编号 \(\le mid\)

这个序列的构造非常容易,我们在构造链划分的时候构造出的链天然就满足这个顺序。只要依次按照这个顺序确定每个链顶的父亲即可。这样最后一步也在 \(O(n\log n)\) 的时间内完成。

至此我们完成了树的部分的求解。一般图的情况是类似的:分为找生成树以及确定非树边即可。

找生成树的时候直接调用树的部分:唯一需要修改的是寻找 \(0\rightarrow u\) 的路径的部分,我们需要解决路径不唯一带来的一些干扰。

解决方法是,我们使用同样的二分手法。固定之前已确定在生成树上的边用远出现在询问里。然后二分 \(mid\),ban 掉 \(\le mid\) 的未出现的边。这样我们会得到一个二分结果 \(x\),它的真实意义是:不存在一条 \(0\rightarrow u\) 的路径,使得编号全都 \(\gt x\),但存在编号全部 \(\ge x\) 的。我们接下来加入边 \(x\),然后将 \(\lt x\) 的未出现的边全部强制成未出现的状态。最后即可求出一条 \(0\rightarrow u\) 的路径(并且可以发现,求出的是一个,边的标号尽可能大的解)。

接下来考虑找非树边:使用从下至上剥叶子的方法,这里也分两步走:找到一个点 \(u\) 相连的非树边的编号集合,以及找到另一个端点。

第二步的做法和树的情况中寻找链顶的父亲一致:在一个满足父亲总出现在儿子之前的序列上二分即可。

其实所谓的剥叶子也不能自由地剥,自然的实现方式是倒序扫描这个“父亲总在儿子之前”的序列,然后考虑其相连的非树边。

唯一遗留的问题就是找到相连的非树边点集了。我们断开 \(u\) 和父亲的连边,然后保留其余的树边。此时一条树边和 \(u\) 相连的充要条件是:加入之后,\((0,u)\) 重新连通。同样可以二分之。

到这里我们一共有多少个二分了?总之在 \(O(n\log n)\) 的询问次数内解决了。

可以参考一下代码实现,我觉得我写的还是比较结构化的。

代码

posted on 2024-01-08 20:22  Cry_For_theMoon  阅读(232)  评论(2编辑  收藏  举报