[CF2065H] Bro Thinks He's Him 题解
首先,段数等于相邻不等的数量加一。
如:0011101101 中,有 \((2,3),(5,6),(6,7),(8,9),(9,10)\) 相邻不等,于是段数为 \(6\)。
有了这个转化,我们就可以拆贡献到每一组【相邻不等】上了。
我们计算一个下标 \(i\) 的贡献:
找到一个 \(j \in [1,i)\) 且 \(a_j \ne a_i\),我们要让这一组 \(i,j\) 在一些子序列中是相邻的。显然,\(i,j\) 必选,\(i,j\) 之间的不选,\(j\) 之前或 \(i\) 之后的任选,于是贡献就是 \(2^{j-1} \times 2^{n-i}\)。
所以 \(i\) 的贡献就是 \(\sum_{1 < j \le i} ( \left [a_i \ne a_j \right ] 2 ^ {j-1} \times 2 ^ {n-i}) = 2 ^ {n-i}\sum_{1 < j \le i} \left [a_i \ne a_j \right ] 2 ^ {j-1}\)。这个式子是很好算的,得到初始答案。
那修改怎么处理呢?
我们考虑一次修改会对答案产生哪些变动。
先考虑 \(1 \le j < i\) 且 $ a_i \ne a_j$:
对于 \(i\) 的 flip 操作,\(j\) 会损失 \(2^{j-1} \times 2 ^{n-i}\) 的贡献,所有的 \(j\) 一共损失 \(2 ^ {n-i}\sum_{1 < j \le i} \left [a_i \ne a_j \right ] 2 ^ {j-1}\);
同理,翻转之后(\(a_i \gets \neg a_i\),\(a_i\) 不是原来的 \(a_i\) 了)会加上 \(2^{j-1} \times 2 ^{n-i}\) 的贡献,总共加上了。\(2 ^ {n-i}\sum_{1 < j \le i} \left [a_i \ne a_j \right ] 2 ^ {j-1}\);
我们思考如何用数据结构优化这件事。
我们要做的事情就是维护 \(i\) 之后与 \(a_i\) 不相等的下标 \(j\) 的 \(2^{j-1}\) 之和。
可以开两个线段树 tr[2](树状数组),这两个分别表示前缀中,\(a_j=0\) 的 \(2^{j-1}\) 之和和 \(a_j=1\) 的 \(2^{j-1}\) 之和。每一次 \(i\) 修改 \(0\) 到 \(1\) 就是将 tr[0] 单点减 \(2^{i-1}\),tr[1] 单点加 \(2^{i-1}\)。
\(1\) 到 \(0\) 同理。
算贡献变动就是将 \(\sum_{1 < j \le i} \left [a_i \ne a_j \right ] 2 ^ {j-1}\) 转化成 tr[a[i]^1] 的前缀查询。
\(j < i \le n\) 同理。
统一使用树状数组实现,后缀和的查询就是将下标关于 \([1,n]\) 的中点对称一下。
时间复杂度 \(O(n + q \log n)\)。
#include <bits/stdc++.h>
using namespace std;
//#define filename "xxx"
#define FileOperations() freopen(filename".in", "r", stdin), freopen(filename".out", "w", stdout)
#define multi_cases 1
#define inf 0x3f3f3f3f
#define Linf 0x3f3f3f3f3f3f3f3f
#define pii pair<int, int>
#define all(v) v.begin(), v.end()
template<class T> bool vmax(T &a, T b) { return b > a ? a = b, true : false; }
template<class T> bool vmin(T &a, T b) { return b < a ? a = b, true : false; }
template<class T> void clear(T &x) { T().swap(x); }
#define int long long
const int N = 2e5+2, P = 998244353;
void reduce(int &a) { if(a >= P) a -= P; }
void enlarge(int &a) { if(a < 0) a += P; }
void vadd(int &a, int b) { a += b, reduce(a); }
void vsub(int &a, int b) { a -= b, enlarge(a); }
int pw[N];
char s[N];
int n, a[N];
struct BIT {
int n, c[N];
void init(int n = 0) {
this->n = n;
memset(c, 0, sizeof(int) * (n+1));
}
void add(int idx, int v) { for(; idx <= n; idx += idx & -idx) vadd(c[idx], v); }
int query(int idx) {
int res = 0;
for(; idx; idx -= idx & -idx) vadd(res, c[idx]);
return res;
}
} pretr[2], suftr[2];
int calc() {
int ans = pw[n] - 1, c[2] = { };
for(int i = 1; i <= n; ++i) {
vadd(ans, c[a[i] ^ 1] * pw[n-i] % P);
vadd(c[a[i]], pw[i-1]);
}
return ans;
}
void Traveller() {
scanf("%s", s+1);
n = strlen(s+1);
for(int i = 1; i <= n; ++i) a[i] = s[i] ^ 48;
pw[0] = 1;
for(int i = 1; i <= n; ++i) reduce(pw[i] = pw[i-1] << 1);
pretr[0].init(n), pretr[1].init(n);
for(int i = 1; i <= n; ++i) pretr[a[i]].add(i, pw[i-1]);
suftr[0].init(n), suftr[1].init(n);
for(int i = 1; i <= n; ++i) suftr[a[i]].add(n-i+1, pw[n-i]);
int ans = calc(), q;
scanf("%lld", &q);
for(int i = 1, p; i <= q; ++i) {
scanf("%lld", &p);
vsub(ans, pw[n-p] * pretr[a[p] ^ 1].query(p) % P);
pretr[a[p]].add(p, -pw[p-1] + P);
vsub(ans, pw[p-1] * suftr[a[p] ^ 1].query(n-p+1) % P);
suftr[a[p]].add(n-p+1, -pw[n-p] + P);
a[p] ^= 1;
vadd(ans, pw[n-p] * pretr[a[p] ^ 1].query(p) % P);
pretr[a[p]].add(p, pw[p-1]);
vadd(ans, pw[p-1] * suftr[a[p] ^ 1].query(n-p+1) % P);
suftr[a[p]].add(n-p+1, pw[n-p]);
printf("%lld ", ans);
}
puts("");
}
signed main() {
#ifdef filename
FileOperations();
#endif
signed _ = 1;
#ifdef multi_cases
scanf("%d", &_);
#endif
while(_--) Traveller();
return 0;
}

浙公网安备 33010602011771号