1 条题解
-
0
自动搬运
来自洛谷,原作者为

飞雨烟雁
尽管我们走不了最短路,但图仍是连通图,TLE之前,没有一个节点叫失败。搬运于
2025-08-24 22:33:23,当前版本为作者最后更新于2024-05-24 19:09:45,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
我来补充一些官方题解没提到的细节,比如说为什么能用 Berlekamp–Massey 算法,以及那个神秘数字 是怎么出现的。
朴素做法
首先我们明确一下动态规划是怎么做的。
我们设 为长度为 且最后 位恰为字符串 的合法 序列个数。举个例子, 时 ,仅有的两种情况为 和 。
按这样 dp 的时间复杂度是 的。但是我们真的有必要把全部 个 拿来 dp 吗?
当然不用,比如当 时, 始终是 ,因为它已经有两个零了,无论后一位是什么,都不可能恰好有 或 个零。也就是说,如果 中零的个数超过了 ,那我们没有必要考虑它。
除此之外, 时, 也始终是 。原因是类似的。当 中零的个数低于 ,我们也没有必要考虑。
综上,我们只需考虑那些恰好含有 个零或含 个零的字符串,一共有 $m=\binom {k-1}{a-1}+\binom {k-1}{a}+\binom {k-1}{a+1}$ 个。
dp 结束后,我们要怎么得到答案呢?我们记答案为 ,则有含有 个零的 对答案的贡献为 (因为它的下一位必须填零),含 个零的 的贡献为 (因为下一位填什么都行),含 个零的 的贡献为 (下一位只能为一)。
在本题的数据范围之内, 时 取得最大值,恰为 。下面,我们通过一个定理,来解释 与最短递推式长度之间的关系。
状态集递推定理
给定有限集 及系数 ,若 有递推式 ,则数列 是至多 阶的线性递推数列。
详细证明见 数学杂记【86 - 100】 中的第 88 条,此外也可以用 Cayley-Hamilton 定理去证明。
在这道题里,这个有限集 就是那 个字符串构成的集合。套用该定理可得:答案序列 是至多 阶的线性递推数列,即最短递推式的长度不超过 。
这样,我们就证明了这道题确实可以用 Berlekamp–Massey 算法来求解,并且最短递推式长度不超过 。那么我们就可以先 dp 求出 时的 ,然后上 BM 算法求解。
总时间复杂度为 ,核心代码如下:
int InvList[Mx], List[Mx], cnt; // [1, cnt] int ZeroNum[Mx]; void Dfs(int Num, int ZeroCnt, int Pos, int a){ if(!Pos){ if(ZeroCnt - a <= 1 && ZeroCnt - a >= -1){ List[++cnt] = Num, InvList[Num] = cnt; ZeroNum[cnt] = ZeroCnt; } return; } Dfs(Num << 1, ZeroCnt + 1, Pos - 1, a); Dfs(Num << 1 | 1, ZeroCnt, Pos - 1, a); } int Dp[2][Mx], Now; // k, k + 1, ..., k + 2 * cnt ll Ans[Mx]; void Dfs2(int Num, int ZeroCnt, int Pos, int a, int k){ if(!Pos){ if(ZeroCnt == a || ZeroCnt == a + 1) ++Ans[k], ++Dp[0][InvList[Num >> 1]]; return; } Dfs2(Num << 1, ZeroCnt + 1, Pos - 1, a, k); Dfs2(Num << 1 | 1, ZeroCnt, Pos - 1, a, k); } int Solve(int n, int k, int a){ if(k == 1) return FastPow(2, n); cnt = 0, Now = 1; for(int i = 0; i < Mx; ++i) InvList[i] = List[i] = ZeroNum[i] = Ans[i] = Dp[0][i] = Dp[1][i] = 0; Dfs(0, 0, k - 1, a), Dfs2(0, 0, k, a, k); for(int i = k; i < k + cnt * 2; ++i){ ll Sum1 = 0; for(int j = 1; j <= cnt; ++j) Dp[Now][j] = 0; for(int j = 1; j <= cnt; ++j){ if(ZeroNum[j] == a) Sum1 += (Dp[Now ^ 1][j] << 1); else Sum1 += Dp[Now ^ 1][j]; if(ZeroNum[j] == a + 1){ Dp[Now][InvList[List[j] >> 1 | (1 << (k - 2))]] = (Dp[Now][InvList[List[j] >> 1 | (1 << (k - 2))]] + Dp[Now ^ 1][j]) % Mod; } else if(ZeroNum[j] == a){ Dp[Now][InvList[List[j] >> 1 | (1 << (k - 2))]] = (Dp[Now][InvList[List[j] >> 1 | (1 << (k - 2))]] + Dp[Now ^ 1][j]) % Mod; Dp[Now][InvList[List[j] >> 1]] = (Dp[Now][InvList[List[j] >> 1]] + Dp[Now ^ 1][j]) % Mod; } else{ Dp[Now][InvList[List[j] >> 1]] = (Dp[Now][InvList[List[j] >> 1]] + Dp[Now ^ 1][j]) % Mod; } } Ans[i + 1] = Sum1 % Mod, Now ^= 1; } if(n <= k + 2 * cnt) return Ans[n]; return BMBM(Ans + k - 1, 2 * cnt + 1, n - k); // BMBM 用于求最短递推式,并返回数列远端的值 }
Remark
- 1
信息
- ID
- 4639
- 时间
- 5000ms
- 内存
- 512MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者