题目大意
题面
Alice 手上有两个长度为 N 的二进制字符串 A 和 B(仅由字符 0 和 1 组成)。
Alice 玩如下游戏。初始时她的分数为 0。每次迭代:
-
她把两个字符串的首字符乘积加到分数上,即分数增加 A1⋅B1。
-
如果 A1=B1,就把 A 替换为 left_shift(A)。
-
否则把 B 替换为 left_shift(B)。
对于二进制字符串 S=S1S2…SL,left_shift(S) 定义为删去首字符并追加到末尾:
left_shift(S)=S2S3…SLS1
现在有 Q 次独立询问。第 i 次询问给出整数 Ki,需要求出恰好执行 Ki 次迭代后 Alice 的分数。各次询问彼此独立,每次都视为从初始字符串 A、B 重新开始。
输入格式
-
第一行一个整数 T,表示测试用例组数。
-
每组测试用例共四行:
- 第一行两个整数 N、Q,分别表示字符串长度与询问次数。
- 第二行是二进制字符串 A。
- 第三行是二进制字符串 B。
- 第四行 Q 个空格分隔的整数 K1,K2,…,KQ。
输出格式
对每组测试用例输出一行,包含 Q 个空格分隔的整数,第 i 个整数是执行 Ki 次迭代后 Alice 的分数。
数据范围
-
1≤T≤104
-
1≤N,Q≤106
-
1≤Ki≤1012
-
所有测试用例的 N 之和、Q 之和各自不超过 106
-
时间限制 3 秒;内存限制 1.5 GB
样例
样例 1
输入:
1 2 3 4 5 6 7 8 9
| 2 2 5 01 10 1 3 5 7 9 3 4 101 100 1 6 7 12
|
输出:
样例解释
样例 1 的第一组测试用例,前 5 次迭代的详细过程:
-
A=01, B=10。分数增加 0⋅1=0。此时 A1=B1,令 B←left_shift(B)=01。
-
A=01, B=01。分数增加 0⋅0=0。此时 A1=B1,令 A←left_shift(A)=10。
-
A=10, B=01。分数增加 1⋅0=0。此时 A1=B1,令 B←left_shift(B)=10。
-
A=10, B=10。分数增加 1⋅1=1。此时 A1=B1,令 A←left_shift(A)=01。
-
A=01, B=10。分数增加 0⋅1=0。此时 A1=B1,令 B←left_shift(B)=01。
前 1,3,5,7,9 次迭代后的累计分数依次为 0,0,1,1,2。
思路讲解
一句话
K 最大到 1012,逐步模拟必炸;这题的结构是「run-length 周期 + 首块 + 整圈 + 零头」三段式,外面再套个二分。
状态机:matched / mismatched
得分 A1⋅B1 只有在 A1=B1=1 时是 1,其他都是 0。而且「谁前进」完全绑定在 A1=?B1 上,所以过程天然分两态:
matched 段持续到 A 跨过一个 A-run(A1 翻转),mismatched 段持续到 B 跨过一个 B-run(B1 翻转),两态在 run 边界交替。
🎬 Note: 动画:两态循环切换(对应下方 BasicLoop.mp4)——沿时间轴展示 matched ↔ mismatched 的交替节拍,以及每步谁在动、谁在得分。
Video

Phase:一个 matched + 一个 mismatched
把相邻的 matched + mismatched 合成一个 phase。每个 phase 吃掉 A 的 1 个 run + B 的 1 个 run,步数 =len(A-run)+len(B-run)。
关键不变量:
每过一个 phase,A1,B1 各翻 1 次,相对关系还原——所以所有 phase 的内部子结构都和第 1 个一样,只需判一次 cA=?cB(cA,cB 就是 A1,B1 的初始值)。后面零头分类直接吃这条不变量。
跳过首块 + 周期性
A 从首块内部起步,所以首块是残缺的(长度 L0A,与 A1 同字符的极长前缀)。跳过首块后从 L0AmodN 开始把 A 当循环串扫一圈,得到完整 run 长度序列 r0A,r1A,…,rkA−1A,段长总和 =N,之后按段数 kA 为周期重复。B 侧同构。
注意 段数 kA 和段数 kB 一般不相等,两套预处理彼此独立——同一个 P(已过 phase 数)在 A 侧和 B 侧走的圈数、零头段号可以完全不一样。
🎬 Note: 动画:首块拎出 + 整圈跳跃(对应下方 SkipJump.mp4)——可视化「首块 L0A 单独算一份 + 后面按段数周期扫圈」的分解。
Video


三段式 O(1) 函数
走完 P 个 phase = phase 1 吃首块 + 剩下 P−1 个 phase 按周期跑完。两侧各自拆 P−1=q⋅k+r:
fA(P)=L0A+qA⋅N+pref_a[rA]
fB(P)=L0B+qB⋅N+pref_b[rB]
gA(P)=L0A⋅cA+qA⋅WA+pref_score_A[rA]

WA=∑iriA⋅ratei 是一整圈的得分(ratei 就是第 i 段 A1 的值:偶 i 是 1−cA、奇 i 是 cA)。P=0 要直接 return 0,别套公式——不然首块项会白加,而且 P−1=−1 做下取整和取模在 C++ 里行为还依赖符号约定,徒增隐患。
二分 + 零头
fA(P)+fB(P) 关于 P 单调递增,直接二分找最大 P∗ 使 fA(P∗)+fB(P∗)≤K。单查询 O(logK)。
零头 R=K−(fA(P∗)+fB(P∗)) 落在第 P∗+1 个 phase 里——用那个不变量,查一次 cA=?cB 定案:
| cA vs cB |
phase 内部结构 |
R 步贡献 |
| cA=cB |
先 matched 后 mismatched |
min(R, lenA)⋅rateA |
| cA=cB |
先 mismatched 后 matched |
max(R−lenB, 0)⋅rateA |
口诀:cA=cB 时 A 在前,R 先花在 matched 段;cA=cB 时 B 在前,要先熬过 lenB 步 mismatched 才落到 matched。代码里 A[0] == B[0] 就是在判这个。
A 或 B 全同字符时翻转结构退化(kA=0 之类),奇偶 rate 公式直接出事。把「都 uniform / 只 A uniform / 只 B uniform」三类单独闭式处理绕开。通解写完一定要回头扫一遍退化分支,不然很容易暴毙。
复杂度
预处理 O(N),单次询问 O(logK),总 O(N+QlogK)≈106+106⋅40≈4×107,稳过 3 s。
📎 动画与源码
solution.tex.txt
solution.pdf — xelatex 编译产物(12 页 A4)
AC代码
心路历程(WA,TLE,MLE……)