题意:给一个字符串后面加若干个字符串或询问某个串的出现次数。强在。
标程:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N=1200005; 6 int son[N][2],ch[N][26],tag[N],sum[N],pre[N],fa[N],sl,ans,mask,n,top,q[N],sc,last,l[N]; 7 char s[N]; 8 int is_rt(int x){return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;} 9 void modi(int x,int y){tag[x]+=y;sum[x]+=y;} 10 void down(int x) 11 { 12 if (tag[x]) 13 { 14 if (son[x][0]) modi(son[x][0],tag[x]); 15 if (son[x][1]) modi(son[x][1],tag[x]); 16 tag[x]=0; 17 } 18 } 19 void rot(int x) 20 { 21 int y=fa[x],z=fa[y],l=(son[y][1]==x),r=l^1; 22 if (!is_rt(y)) son[z][(son[z][1]==y)]=x; 23 fa[x]=z;fa[y]=x;fa[son[x][r]]=y; 24 son[y][l]=son[x][r];son[x][r]=y; 25 } 26 void spl(int x) 27 { 28 q[top=1]=x; 29 for (int i=x;!is_rt(i);i=fa[i]) q[++top]=fa[i]; 30 while (top) down(q[top--]); 31 for (int y;!is_rt(x);rot(x)) 32 if (!is_rt(y=fa[x])) 33 if ((son[fa[y]][0]==y)^(son[y][0]==x)) rot(x);else rot(y); 34 } 35 void accs(int x){for (int t=0;x;t=x,x=fa[x]) spl(x),son[x][1]=t;} 36 void link(int x,int y){fa[x]=y;accs(x);spl(x);} 37 void cut(int x,int y){accs(y);spl(y);fa[x]=son[y][1]=0;}//注意x比y的深度大,x应该在y的右子树 38 void sam(int c) 39 { 40 int p=last,np=++sc;l[np]=l[p]+1;last=np; 41 for (;p&&!ch[p][c];p=pre[p]) ch[p][c]=np; 42 if (!p) pre[np]=1,link(np,1);//注意区分lct和Sam上的father不同 43 else { 44 int q=ch[p][c]; 45 if (l[q]==l[p]+1) pre[np]=q,link(np,q); 46 else { 47 int nq=++sc;l[nq]=l[p]+1; 48 memcpy(ch[nq],ch[q],sizeof(ch[q])); 49 spl(q);sum[nq]=sum[q]; 50 pre[nq]=pre[q]; 51 cut(q,pre[q]);link(nq,pre[q]); 52 pre[q]=pre[np]=nq; 53 link(q,nq);link(np,nq); 54 for (;p&&ch[p][c]==q;p=pre[p]) ch[p][c]=nq; 55 } 56 } 57 accs(np);spl(np); 58 modi(np,1);//np为关键点 59 } 60 void secret(char *s,int mask) 61 { 62 sl=strlen(s); 63 for (int i=0;i<sl;i++) 64 mask=(mask*131+i)%sl,swap(s[i],s[mask]); 65 } 66 void qry(char *s) 67 { 68 int now=1;sl=strlen(s); 69 for (int i=0;i<sl;i++) now=ch[now][s[i]-'A']; 70 if (!now) puts("0");else spl(now),printf("%d\n",ans=sum[now]),mask^=ans; 71 } 72 int main() 73 { 74 scanf("%d%s",&n,s);sl=strlen(s); 75 sc=last=1; 76 for (int i=0;i<sl;i++) sam(s[i]-'A'); 77 char op[10]; 78 while (n--) 79 { 80 scanf("%s%s",op,s);secret(s,mask); 81 if (op[0]=='Q') qry(s); 82 else { 83 sl=strlen(s); 84 for (int i=0;i<sl;i++) sam(s[i]-'A'); 85 } 86 } 87 return 0; 88 }
题解:后缀自动机+lct
如果是离线就是二维数点问题。
在线的话,其实就是求right集合大小嘛,用有根lct动态维护sam的树边。
每次加入一个新点,就是在到根路径上都+1。打一个加标记即可。