http://www.lydsy.com/JudgeOnline/problem.php?id=4866
询问一个字符串区间内有多少子区间重排后能形成回文串。
由于字符集只有26,可以给每个字母分配一个2的幂次作为权值,则相当于询问区间异或和是否为2的幂次或0
直接很难维护,那么考虑莫队,维护一个桶记录当前区间内所有前缀的异或和,若在前端插入删除则打上全局标记,然后每次插入删除时枚举每个2的幂次更新答案即可。时间复杂度n*sqrt(n)*26
还是那句话,BZOJ机子太慢,本地跑得游刃有余,交上去就过不了。。。
那么考虑卡卡常,若字符集小则复杂度低,若字符集大则答案小,那么预处理出以每个点开头或结尾,第一个合法的另一端在哪里,更新答案时若不可能有贡献就直接跳过,这样就可通过了。
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define gm 60005
using namespace std;
typedef long long ll;
struct Istream
{
static const size_t str=1<<16;
char buf[str],*s,*t;
Istream():buf(),s(),t(){}
char get()
{
return (s==t)?(t=buf+fread(s=buf,1,str,stdin),*s++):(*s++);
}
Istream& operator>> (int &x)
{
x=0; register char c;
do c=get(); while(c<'0'||c>'9');
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=get();
return *this;
}
Istream& operator>> (char *s)
{
register char c;
do c=get(); while(c<'a'||c>'z');
while(c>='a'&&c<='z') *s++=c,c=get();
return *this;
}
}cin;
struct Ostream
{
static const size_t str=1<<16;
char buf[str],*s,*t;
Ostream():buf(),s(buf),t(buf+str){}
~Ostream(){fwrite(buf,1,s-buf,stdout);}
void put(char c)
{
(s==t)?(fwrite(s=buf,1,str,stdout),*s++=c):(*s++=c);
}
Ostream& operator<< (ll x)
{
if(!x) return operator<<('0');
int a[22],t=1;
while(x) a[t++]=x%10,x/=10;
while(--t) put(a[t]+'0');
return *this;
}
Ostream& operator<< (char c){return put(c),*this;}
}cout;
const char endl='\n';
int n,m,size;
int pre[gm],nex[gm];
char s[gm];
int t[gm];
ll ans[gm];
struct query
{
int l,r;
ll* ptr;
bool operator< (const query& ano) const
{
return l/size!=ano.l/size?l/size<ano.l/size:r<ano.r;
}
}q[gm];
int l=1,r=0;
ll now=0;
unsigned short cnt[1<<26];
int mark=0;
int tot=0;
int a=0;
int val[128];
inline void append_front(int x)
{
tot^=x; mark^=x; ++cnt[x^mark];
if(nex[l]>r) ++now;
else
{
now+=cnt[mark];
for(int i=0;i<a;++i) now+=cnt[(1<<i)^mark];
}
}
inline void contract_front(int x)
{
if(nex[l-1]>r) --now;
else
{
for(int i=0;i<a;++i) now-=cnt[(1<<i)^mark];
now-=cnt[mark];
}
--cnt[x^mark]; mark^=x; tot^=x;
}
inline void append_back(int x)
{
tot^=x; ++cnt[mark];
if(pre[r]<l) ++now;
else
{
now+=cnt[tot^mark];
for(int i=0;i<a;++i) now+=cnt[(1<<i)^mark^tot];
}
--cnt[mark]; ++cnt[tot^mark];
}
inline void contract_back(int x)
{
--cnt[tot^mark]; ++cnt[mark];
if(pre[r+1]<l) --now;
else
{
for(int i=0;i<a;++i) now-=cnt[(1<<i)^mark^tot];
now-=cnt[tot^mark];
}
--cnt[mark]; tot^=x;
}
int main()
{
cin>>n>>m>>(s+1);
for(int i=1;i<=n;++i)
{
int c=s[i];
if(!val[c]) val[c]=1<<(a++);
t[i]=val[c];
}
int last=0; memset(cnt,0,sizeof cnt);
for(int i=1;i<=n;++i)
{
int kre=last^t[i];
pre[i]=cnt[kre];
for(int j=0;j<a;++j)
{
int pos=cnt[kre^(1<<j)];
if(pos>pre[i]) pre[i]=pos;
}
cnt[last]=i; last=kre;
}
last=0; memset(cnt,0,sizeof cnt);
for(int i=n;i>=1;--i)
{
int kre=last^t[i];
nex[i]=(cnt[kre])?(cnt[kre]):n+1;
for(int j=0;j<a;++j)
{
int pos=cnt[kre^(1<<j)];
if(!pos) pos=n+1;
if(pos<nex[i]) nex[i]=pos;
}
cnt[last]=i; last=kre;
}
size=sqrt(n*a);
for(int i=1;i<=m;++i)
{
cin>>q[i].l>>q[i].r;
q[i].ptr=ans+i;
}
sort(q+1,q+m+1);
memset(cnt,0,sizeof cnt);
for(int i=1;i<=m;++i)
{
int x=q[i].l,y=q[i].r;
while(x<l) append_front(t[--l]);
while(y>r) append_back(t[++r]);
while(x>l) contract_front(t[l++]);
while(y<r) contract_back(t[r--]);
*q[i].ptr=now;
}
for(int i=1;i<=m;++i) cout<<ans[i]<<endl;
return 0;
}