题意
给你一个长度为n的字符串,q次询问一个区间内本质不同的回文串的个数。
解法
考虑用可持久化线段树。
表示区间
的答案。
尝试找到
的递推关系。
我们建出回文树。
注意到,回文树上每个节点到根可以拆成
个等差数列。
对这
个等差数列稍微分析一下。
你会发现每个等差数列对应着一个从
到
的区间加法。
证明需要用到一些回文串border的理论。
相关资料可以看我的另一篇博客:
https://blog.csdn.net/ezoilearner/article/details/84851672
于是时空复杂度
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int type;
int n,q;
#define Maxn 100010
#define V 10000010
char s[Maxn];
struct Node{
int sum;
int ls,rs;
}tree[V];
int root[Maxn],cnt=0;
void Add(int &k,int x,int l,int r,int L,int R){
k=++cnt;
tree[k]=tree[x];
if(l==L&&r==R){
tree[k].sum++;
return;
}
int mid=(l+r)>>1;
if(R<=mid)Add(tree[k].ls,tree[x].ls,l,mid,L,R);
else if(mid<L)Add(tree[k].rs,tree[x].rs,mid+1,r,L,R);
else{
Add(tree[k].ls,tree[x].ls,l,mid,L,mid);
Add(tree[k].rs,tree[x].rs,mid+1,r,mid+1,R);
}
}
int Query(int k,int l,int r,int pos){
if(l==r)return tree[k].sum;
int mid=(l+r)>>1;
if(pos<=mid)return Query(tree[k].ls,l,mid,pos)+tree[k].sum;
else return Query(tree[k].rs,mid+1,r,pos)+tree[k].sum;
}
int maxv[Maxn<<2];
void Modify(int k,int l,int r,int pos,int num){
if(l==r){
maxv[k]=num;
return;
}
maxv[k]=num;
int mid=(l+r)>>1;
if(pos<=mid)Modify(k<<1,l,mid,pos,num);
else Modify(k<<1|1,mid+1,r,pos,num);
}
int Query(int k,int l,int r,int L,int R){
if(l==L&&r==R)return maxv[k];
int mid=(l+r)>>1;
if(R<=mid)return Query(k<<1,l,mid,L,R);
else if(mid<L)return Query(k<<1|1,mid+1,r,L,R);
else return max(Query(k<<1,l,mid,L,mid),Query(k<<1|1,mid+1,r,mid+1,R));
}
int endpos[Maxn];
int ch[Maxn][26],fail[Maxn],anc[Maxn],fa[Maxn],len[Maxn],l=0;
int last=1,tot=1;
inline int getfail(int x){
while(s[l-len[x]-1]!=s[l])x=fail[x];
return x;
}
inline void insert(int dir){
l++;
int tmp=getfail(last);
if(!ch[tmp][dir]){
tot++;
memset(ch[tot],0,sizeof(ch[tot]));
len[tot]=len[tmp]+2;fa[tot]=tmp;
fail[tot]=ch[getfail(fail[tmp])][dir];
ch[tmp][dir]=tot;
anc[tot]=((fail[tot]>1&&len[tot]-len[fail[tot]]==len[fail[tot]]-len[fail[fail[tot]]])?anc[fail[tot]]:tot);
}
last=ch[tmp][dir];
}
int head[Maxn],v[Maxn],nxt[Maxn],T=0;
int in[Maxn],out[Maxn],dfk=0;
inline void add_edge(int s,int e){T++;v[T]=e;nxt[T]=head[s];head[s]=T;}
void dfs(int u){
in[u]=++dfk;
for(int i=head[u];i;i=nxt[i])
dfs(v[i]);
out[u]=dfk;
}
inline void Build_Tree(){
for(register int i=1;i<=n;++i){
insert(s[i]-'a');
endpos[i]=last;
}
add_edge(1,0);
for(register int i=2;i<=tot;++i)add_edge(fail[i],i);
dfs(1);
for(register int i=1;i<=n;++i){
root[i]=root[i-1];
for(register int k=endpos[i];k>1;k=fail[anc[k]]){
int l=max(Query(1,1,dfk,in[k],out[k])-len[k]+1,0)+1;
int r=i-len[anc[k]]+1;
Add(root[i],root[i],1,n,l,r);
}
Modify(1,1,dfk,in[endpos[i]],i);
}
}
inline void rd(int &x){
x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
}
int main(){
rd(type);
rd(n);rd(q);
memset(ch[0],0,sizeof(ch[0]));
memset(ch[1],0,sizeof(ch[1]));
fail[0]=1;len[1]=-1;
anc[0]=0;anc[1]=1;s[0]=-1;
scanf("%s",s+1);
Build_Tree();
int Ans=0,l,r;
while(q--){
rd(l);rd(r);
l=l^(type*Ans);r=r^(type*Ans);
printf("%d\n",Ans=Query(root[r],1,n,l));
}
return 0;
}