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]);
}

浙公网安备 33010602011771号