P3604 美好的每一天

P3604 美好的每一天

给定一个字符串,每次询问其一个子区间有多少个子区间重排后可以构成回文串。

我们思考“重排后构成回文串”的性质,即:所有字符的出现次数至多有一个奇数。

观察到“出现次数”以及可以离线以及数据范围,我们可以想到使用莫队来解决这个问题。

我们可以通过状压来表示每一个字符当前这个可以得到结果的奇偶性,以及记录一下当前答案 \(Now\)

然后我们发现每次单点修改的时候会增加所有的前缀/后缀区间,删除也一样。

于是可以考虑求一遍区间前缀异或和再来维护,就变成点对点的询问/修改了。

然后考虑每一个点会增加的贡献:当前前缀异或和桶 \(pre[x]^(1<<i)\) \((i\in [0,25])\) 的值的个数。

那么这道题就结束了,注意卡空间,要开 short int。

代码:

#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
	x=0;char ch=getchar();bool f=false;
	while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	x=f?-x:x;
	return ;
}
template <typename T>
inline void write(T x){
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10^48);
	return ;
}
const int N=4e6+5;
#define ll long long
int n,m,t;
short int cnt[1<<26];
int pre[N],Ans[N];
struct Query{int l,r,id;}Q[N];
char str[N];
int Now;
#define bl(i) (i-1)/t+1
inline void Add(int x){
	int y=pre[x];
	Now+=cnt[y];
	for(int i=0;i<=25;i++) Now+=cnt[y^(1<<i)];
	cnt[y]++;
	return ;
}
inline void Del(int x){
	int y=pre[x];
	cnt[y]--;
	Now-=cnt[y];
	for(int i=0;i<=25;i++) Now-=cnt[y^(1<<i)];
	return ;
}
inline bool Cmp(Query x,Query y){return bl(x.l)^bl(y.l)?bl(x.l)<bl(y.l):bl(x.l)&1?x.r<y.r:x.r>y.r;}
int main(){
	read(n);read(m);
	scanf("%s",str+1);
	for(int i=1;i<=n;i++) pre[i]=pre[i-1]^(1<<(str[i]-'a'));
	t=sqrt(m);
	for(int i=1;i<=m;i++) read(Q[i].l),read(Q[i].r),Q[i].id=i,Q[i].l--;
	sort(Q+1,Q+m+1,Cmp);
	int l=1,r=0;
	for(int i=1;i<=m;i++){
		while(l>Q[i].l) Add(--l);
		while(r<Q[i].r) Add(++r);
		while(l<Q[i].l) Del(l++);
		while(r>Q[i].r) Del(r--);
		Ans[Q[i].id]=Now;
	}
	for(int i=1;i<=m;i++) write(Ans[i]),putchar('\n');
	return 0;
} 
posted @ 2021-04-21 09:03  __Anchor  阅读(35)  评论(0编辑  收藏  举报