AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=2555
【题解】
我们把模板串建成SAM,那么一个串的出现次数就是在对应状态上的Right集的大小。
那么我们如何在线维护Right集的大小呢?可以用LCT/平衡树+dfs序
反正我用的是LCT,毕竟好写。
我们用LCT维护parent树,对于新加入的点,连一条通向parent的边即可。
注意:如果在LCT中使用了reverse操作,即把某点定为了原树的根,那么在回答询问时要重新将1结点定为根。
我的LCT代码中省去了reverse操作。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<string>
#include<algorithm>
using namespace std;
#define FILE "read"
#define MAXN 1200010
#define up(i,j,n) for(int i=j;i<=n;++i)
#define dn(i,j,n) for(int i=j;i>=n;--i)
#define cmax(a,b) a=max(a,b)
#define cmin(a,b) a=min(a,b)
int Q,n,cnt(1),now(1),mask,ans,f[MAXN],vis[MAXN],delt[MAXN],size[MAXN],mx[MAXN],stack[MAXN],pre[MAXN],son[MAXN][2],c[MAXN][2];
char ch[MAXN],s[MAXN];
string chars;
bool get(int x){return son[f[x]][1]==x;}
bool isroot(int x){return son[f[x]][0]!=x&&son[f[x]][1]!=x;}
void Tsize(int x,int v){if(!x) return; delt[x]+=v; size[x]+=v;}
void pushdown(int x){
if(delt[x]){
Tsize(son[x][0],delt[x]); Tsize(son[x][1],delt[x]);
delt[x]=0;
}
}
void rotate(int x){
int y=f[x],z=f[y],which=get(x);
if(!isroot(y)) son[z][son[z][1]==y]=x;
son[y][which]=son[x][which^1]; f[son[y][which]]=y;
son[x][which^1]=y; f[y]=x; f[x]=z;
son[0][0]=son[0][1]=f[0]=size[0]=0;
}
void splay(int x){
int top(0); stack[++top]=x;
for(int i=x;!isroot(i);i=f[i]) stack[++top]=f[i];
dn(i,top,1) pushdown(stack[i]);
for(int y=f[x];!isroot(x);rotate(x),y=f[x])
if(!isroot(y)) rotate(get(x)==get(y)?y:x);
}
void access(int x){for(int t(0);x;t=x,x=f[x])splay(x),son[x][1]=t;}
void linkk(int x,int y){f[x]=y;access(y);splay(y);Tsize(y,size[x]);}
void cut(int x){access(x);splay(x);Tsize(son[x][0],-size[x]);f[son[x][0]]=0;son[x][0]=0;}
void insert(int x){
int p=now,np=++cnt;
mx[np]=mx[now]+1; now=np; size[np]=1;
while(!c[p][x]&&p) c[p][x]=np,p=pre[p];
if(!p) pre[np]=1,linkk(np,1);
else{
int q=c[p][x];
if(mx[q]==mx[p]+1) pre[np]=q,linkk(np,q);
else{
int nq=++cnt;
mx[nq]=mx[p]+1;
memcpy(c[nq],c[q],sizeof(c[q]));
pre[nq]=pre[q]; linkk(nq,pre[q]);
pre[np]=pre[q]=nq; cut(q); linkk(q,nq); linkk(np,nq);
while(c[p][x]==q&&p) c[p][x]=nq,p=pre[p];
}
}
}
void Load(int mask){
scanf("%s",s);
chars=s;
up(i,0,chars.length()-1){
mask=(mask*131+i)%chars.length();
char t=chars[i];
chars[i]=chars[mask];
chars[mask]=t;
}
}
int main(){
freopen(FILE".in","r",stdin);
freopen(FILE".out","w",stdout);
scanf("%d",&Q); scanf("%s",ch);
n=strlen(ch);
up(i,0,n-1) insert(ch[i]-'A');
up(i,1,Q){
char opt[10]; scanf("%s",opt); Load(mask);
if(opt[0]=='A') up(j,0,chars.length()-1)
insert(chars[j]-'A');
else{
int x=1;
up(j,0,chars.length()-1){
if(c[x][chars[j]-'A']) x=c[x][chars[j]-'A'];
else{x=0;break;}
}
if(!x) ans=0;
else splay(x),ans=size[x];
mask^=ans;
printf("%d\n",ans);
}
}
return 0;
}
附上makedata程序,供对拍使用:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<string>
#include<algorithm>
using namespace std;
#define FILE "read"
#define MAXN 1200010
#define up(i,j,n) for(int i=j;i<=n;++i)
#define dn(i,j,n) for(int i=j;i>=n;--i)
#define cmax(a,b) a=max(a,b)
#define cmin(a,b) a=min(a,b)
char ch[2]={'A','B'};
string s[2]={"QUERY","ADD"};
int main(){
freopen(FILE".in","w",stdout);
srand(time(NULL));
int Q=10,n=5,m=5;
printf("%d\n",Q);
up(i,1,n) printf("%c",ch[rand()%2]);
printf("\n");
up(i,1,Q){
int t=rand()%2;
if(t==0){
cout<<s[t]<<' '; int len=rand()%m+1;
up(j,1,len) printf("%c",ch[rand()%2]);
}
else{
cout<<s[t]<<' '; int len=rand()%m+1;
up(j,1,len) printf("%c",ch[rand()%2]);
}
printf("\n");
}
return 0;
}