题目大意
给定一个长度为$n(1 \le n \le 10^5)$的$01$字符串以及两个整数$a$和$b(1 \le a, b \le 10 ^ 5)$,$1$表示这一小局$team \ A$获胜,$0$则表示$team \ B$获胜。当一个$team$获胜$a$小局后,记作获胜一大局,并将小局比分清空。获胜$b$大局后则赢得最终胜利。问将这个字符串以第$i(1 \le i \le n)$位作为起点开始循环时,最终获胜的队伍是不是$team$,若是输出$1$,否则输出$0$。
解题思路
考虑对于每个起点$i$预处理出从这个点开始完成一大局比赛后第二局比赛的起点,以及这一大局$team\ A$是否获胜。自然想到再用倍增预处理出进行$2 ^ 1,2^2,2^3,\dots$大局后$team\ A$获胜的次数。然后处理询问,只需要从最高位往下遍历凑出有一个队获胜的条件即可。至于预处理大局的起点,可以联想到双指针,因为每一小局在$1$~$n$上是连续的。
最后将上面的想法整理起来即可。
参考代码 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 51 52 53 54 55 #include <bits/stdc++.h> #define maxn 100100 #define int long long using namespace std;const double eps = 1e-8 ;int f[maxn][22 ], sum[maxn][22 ];void solve () { int n, a, b; cin >> n >> a >> b; string s; cin >> s; int cnta = 0 , cntb = 0 ; for (int i = 0 , j = 0 ; i < n; ++i) { while (cnta < a && cntb < a) { if (s[j] == '0' ) cntb++; else cnta++; j = (j + 1 ) % n; } f[i][0 ] = j; sum[i][0 ] = (cnta > cntb); if (s[i] == '0' ) cntb--; else cnta--; } for (int j = 1 ; j < 20 ; ++j) { for (int i = 0 ; i < n; ++i) { sum[i][j] = sum[i][j - 1 ] + sum[f[i][j - 1 ]][j - 1 ]; f[i][j] = f[f[i][j - 1 ]][j - 1 ]; } } for (int i = 0 ; i < n; ++i) { int cur = 0 , pos = i; cnta = 0 , cntb = 0 ; for (int j = 19 ; j >= 0 && cnta < b && cntb < b; --j) { if (cur + (1 << j) > 2 * b - 1 ) continue ; if (cnta + sum[pos][j] > b) continue ; if (cntb + (1 << j) - sum[pos][j] > b) continue ; cnta += sum[pos][j]; cntb += (1 << j) - sum[pos][j]; cur += (1 << j); pos = f[pos][j]; } if (cnta == b) cout << 1 ; else cout << 0 ; } } signed main () { ios::sync_with_stdio (false ); cin.tie (0 ); cout.tie (0 ); int t = 1 ; while (t--) { solve (); } return 0 ; }