把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

luogu P3604 美好的每一天

题面传送门
蛮好的一道题目。
考虑状压。对原数列做前缀和,每一位代表是奇数个还是偶数个。那么前缀和亦或就可以得到一个区间。
对于一个区间,只有亦或和是\(0\)\(2^k\)时才能构成回文串。那么莫队增加和修改时枚举\(k\)即可。
注意前缀和要往前一位。
时间复杂度\(O(26n\sqrt n)\)
注意常数不要太大。
代码实现:

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m,k,q[60039],x,y,ans[60039],tot;
unsigned short f[1<<26];
char _s;
struct yyy{
	int x,y,num;
}s[60039];
inline bool cmp(yyy x,yyy y){
	return (x.x/k==y.x/k)?(((x.x/k)&1)?x.y<y.y:x.y>y.y):x.x<y.x;
}
int main(){
	register int i,j,l=1,r=0,tot=0;
	register yyy tmp;
	scanf("%d%d",&n,&m);
	k=sqrt(n+m);
	for(i=1;i<=n;i++){
		_s=getchar();
		while(_s<'a'||_s>'z') _s=getchar();
		q[i]=q[i-1]^(1<<(_s-'a'));
		//printf("%d ",q[i]);
	}
	for(i=1;i<=m;i++)scanf("%d%d",&s[i].x,&s[i].y),s[i].num=i;
	sort(s+1,s+n+1,cmp);
	f[0]=1;
	for(i=1;i<=m;i++){
		tmp=s[i];
		while(r<tmp.y){
			r++;
			tot+=f[q[r]];
			for(j=0;j<=25;j++) tot+=f[q[r]^(1<<j)];
			f[q[r]]++;
		}
		while(l>tmp.x){
			l--;
			tot+=f[q[l-1]];
			for(j=0;j<=25;j++) tot+=f[q[l-1]^(1<<j)];
			f[q[l-1]]++;
		}
		while(l<tmp.x){
			f[q[l-1]]--;
			for(j=0;j<=25;j++) tot-=f[q[l-1]^(1<<j)];
			tot-=f[q[l-1]];
			l++;
		}
		while(r>tmp.y){
			f[q[r]]--;
			for(j=0;j<=25;j++) tot-=f[q[r]^(1<<j)];
			tot-=f[q[r]];
			r--;
		}
		ans[s[i].num]=tot;
	}
	for(i=1;i<=m;i++) printf("%d\n",ans[i]);
}
posted @ 2020-04-20 12:00  275307894a  阅读(43)  评论(0)    收藏  举报
浏览器标题切换
浏览器标题切换end