果然是我不会的题。现在会了就好。
题面中的序列其实是有名的 Thue-Morse 序列,它有如下三种生成方式:
- 将序列的前半部分取反后拼在后面
- 以 开始,将序列中每个 换成 ,每个 换成 。
在本题中,我们要考虑一个串
是否在该序列中出现,那从第三种生成方式开始思考是比较适合的。
再考虑
的数据范围,这提示我们最终复杂度要么与 无关,要么带个 。
在生成这个序列时,我们将每个
和
进行扩大,那么反过来,我们能不能对 用类似的过程进行缩小呢?
显然是可以的。我们先以
的情况为例,看如何缩小 。
我们可以将
中的相邻元素每两个分为一组,要求组内不相同,然后保留每组内第一个元素。此过程中头尾可能有落单的段,但是我们可以推理出这些段在原序列中所属的组应该是什么。
容易发现可能的分组方式好像有两种,但我们可以证明,当
时,可能的分组方式只有一种。因为如果有两种的话,整个串必然是
相间的,那就一定存在一种分组方式使得缩小后出现了 或 ,这显然是不合法的。
也就是说,不断执行上述的缩小过程,并在 时特判,我们就能判断一个 是否在原序列中出现。
进一步地,一个出现在原序列的,长度
的串,与一个合法缩小过程构成了双射。
考虑拓展到原问题,此时我们只需计数合法缩小过程的个数。设原问题答案为
,如果
就特判,否则把两种分组方式都对
用一次, 会视情况变为 或 。然后向合法的情况递归求解即可。注意我们可能不止递归一边,样例中
的情况便是一例。
对搜索进行记忆化,用 map 保存二元组
的答案。容易发现状态数并不是很多,约为 级别,可以通过本题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| #include <bits/stdc++.h> using namespace std; typedef long long ll; const int P = 1e9 + 9; inline void Plus(int &x,const int &y) { x += y;if(x >= P) x -= P;} map<pair<string,ll>,int> mp; inline int Calc(string s,ll k) { if(s.size() == 1) { if(k <= 2) return k + 1; } if(s.size() == 2 && k <= 1) { if(k == 0) return 1; if(k == 1) return s[0] == s[1] ? 1 : 2; } if(s.size() == 3 && !k) { if(s[0] != s[1] || s[1] != s[2]) return 1; return 0; } if(mp.find(make_pair(s,k)) != mp.end()) return mp[make_pair(s,k)]; int ans = 0; bool OK = 1; string nxt; // 从第一个开始分 for(int i = 0;i + 1 < s.size();i += 2) { if(s[i] == s[i + 1]) OK = 0; nxt += s[i]; } if((s.size()) & 1) nxt += s[(int)s.size() - 1]; if(OK) Plus(ans,Calc(nxt,(s.size() & 1) ? (k >> 1) : (k + 1 >> 1))); OK = 1; nxt = s[0] ^ 1; for(int i = 1;i + 1 < s.size();i += 2) { if(s[i] == s[i + 1]) OK = 0; nxt += s[i]; } if(!(s.size() & 1)) nxt += s[(int)s.size() - 1]; if(OK) Plus(ans,Calc(nxt,(s.size() & 1) ? (k + 1 >> 1) : (k >> 1))); return mp[make_pair(s,k)] = ans; } int main() { int T; cin >> T; while(T--) { string s;ll k; cin >> s >> k; cout << Calc(s,k) << endl; } return 0; }
|