进入博客阅读体验更佳:[Rigged Games (牛客多校3 J) | 付诺の小站 (https://funuo0728.github.io/2024/07/26/Rigged-Games-%E7%89%9B%E5%AE%A2%E5%A4%9A%E6%A0%A13-J/)
Rigged Games
题目大意
给定一个长度为 n ( 1 ≤ n ≤ 1 0 5 ) n(1 \le n \le 10^5) n(1≤n≤105)的 01 01 01字符串以及两个整数 a a a和 b ( 1 ≤ a , b ≤ 1 0 5 ) b(1 \le a, b \le 10 ^ 5) b(1≤a,b≤105), 1 1 1表示这一小局 t e a m A team \ A team A获胜, 0 0 0则表示 t e a m B team \ B team B获胜。当一个 t e a m team team获胜 a a a小局后,记作获胜一大局,并将小局比分清空。获胜 b b b大局后则赢得最终胜利。问将这个字符串以第 i ( 1 ≤ i ≤ n ) i(1 \le i \le n) i(1≤i≤n)位作为起点开始循环时,最终获胜的队伍是不是 t e a m team team,若是输出 1 1 1,否则输出 0 0 0。
解题思路
考虑对于每个起点 i i i预处理出从这个点开始完成一大局比赛后第二局比赛的起点,以及这一大局 t e a m A team\ A team A是否获胜。自然想到再用倍增预处理出进行 2 1 , 2 2 , 2 3 , … 2 ^ 1,2^2,2^3,\dots 21,22,23,…大局后 t e a m A team\ A team A获胜的次数。然后处理询问,只需要从最高位往下遍历凑出有一个队获胜的条件即可。至于预处理大局的起点,可以联想到双指针,因为每一小局在 1 1 1~ n n n上是连续的。
最后将上面的想法整理起来即可。
参考代码
#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--;
// cout << sum[i][0] << ' ' << f[i][0] << '\n';
}
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];
}
// cout << cnta << ' ' << cntb << '\n';
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;
}