见证者为见证而来,铭记者因铭记而生|

园龄:粉丝:关注:

[CF2065H] Bro Thinks He's Him 题解

首先,段数等于相邻不等的数量加一。
如:0011101101 中,有 (2,3),(5,6),(6,7),(8,9),(9,10) 相邻不等,于是段数为 6
有了这个转化,我们就可以拆贡献到每一组【相邻不等】上了。

我们计算一个下标 i 的贡献:
找到一个 j[1,i)ajai,我们要让这一组 i,j 在一些子序列中是相邻的。显然,i,j 必选,i,j 之间的不选,j 之前或 i 之后的任选,于是贡献就是 2j1×2ni
所以 i 的贡献就是 1<ji([aiaj]2j1×2ni)=2ni1<ji[aiaj]2j1。这个式子是很好算的,得到初始答案。

那修改怎么处理呢?
我们考虑一次修改会对答案产生哪些变动。
先考虑 1j<iaiaj
对于 i 的 flip 操作,j 会损失 2j1×2ni 的贡献,所有的 j 一共损失 2ni1<ji[aiaj]2j1
同理,翻转之后(ai¬aiai 不是原来的 ai 了)会加上 2j1×2ni 的贡献,总共加上了。2ni1<ji[aiaj]2j1

我们思考如何用数据结构优化这件事。
我们要做的事情就是维护 i 之后与 ai 不相等的下标 j2j1 之和。
可以开两个线段树 tr[2](树状数组),这两个分别表示前缀中,aj=02j1 之和和 aj=12j1 之和。每一次 i 修改 01 就是将 tr[0] 单点减 2i1tr[1] 单点加 2i1
10 同理。

算贡献变动就是将 1<ji[aiaj]2j1 转化成 tr[a[i]^1] 的前缀查询。

j<in 同理。

统一使用树状数组实现,后缀和的查询就是将下标关于 [1,n] 的中点对称一下。

时间复杂度 O(n+qlogn)

#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;
}
posted @   wfc284  阅读(9)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起