【BZOJ 2434】 [Noi2011]阿狸的打字机 fail树+树状数组
就是考了一个fail树的神奇应用我们建出fail树之后,发现我们就是在求y到根的路径上所有的点在以x为根的子树里的个数,这个我们离线后用树状数组+dfs序即可解决
#include <cstdio> #include <cstring> #include <iostream> #include <vector> const int N=100010; char s[N]; struct Trie{ int ch[26],fail,fa,deep; }node[N]; std::vector<int> mem[N]; int sz; struct V{ int to,next; }c[N]; struct VQ{ int to,next,id; }ques[N]; int head[N],t,num,ques_head[N],ques_t; int belong[N],q[N],l[N],r[N],Time; int T[N],ans[N],m,n; inline int Q(int pos){ int ret=0; for(;pos>0;pos-=pos&(-pos)) ret+=T[pos]; return ret; } inline void U(int pos,int key){ for(;pos<=n;pos+=pos&(-pos)) T[pos]+=key; } inline void add(int x,int y){ c[++t].to=y,c[t].next=head[x],head[x]=t; } inline void ques_add(int x,int y,int z){ ques[++ques_t].to=y,ques[ques_t].next=ques_head[x],ques_head[x]=ques_t,ques[ques_t].id=z; } void dfs(int x){ l[x]=++Time; for(int i=head[x];i;i=c[i].next) dfs(c[i].to); r[x]=Time; } void dfs_(int x){ U(l[x],1); for(int i=0;i<mem[x].size();i++) for(int j=ques_head[mem[x][i]];j;j=ques[j].next) ans[ques[j].id]=Q(r[belong[ques[j].to]])-Q(l[belong[ques[j].to]]-1); for(int i=0;i<26;i++) if(node[node[x].ch[i]].deep>node[x].deep) dfs_(node[x].ch[i]); U(l[x],-1); } int main(){ scanf("%s",s); int now=0; for(int i=0;s[i];i++){ if(s[i]=='P'){ belong[++num]=now; mem[now].push_back(num); continue; } if(s[i]=='B'){ now=node[now].fa; continue; } if(!node[now].ch[s[i]-'a'])node[now].ch[s[i]-'a']=++sz,node[sz].fa=now,node[sz].deep=node[now].deep+1; now=node[now].ch[s[i]-'a']; } n=sz+1; q[0]=0; for(int i=0,j=0;i<=j;i++) for(int l=0;l<26;l++) if(node[q[i]].ch[l]){ q[++j]=node[q[i]].ch[l]; node[q[j]].fail=q[i]?node[node[q[i]].fail].ch[l]:0; add(node[q[j]].fail,q[j]); }else node[q[i]].ch[l]=q[i]?node[node[q[i]].fail].ch[l]:0; dfs(0); scanf("%d",&m); for(int i=1,x,y;i<=m;i++) scanf("%d%d",&x,&y),ques_add(y,x,i); dfs_(0); for(int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; }
苟利国家生死以, 岂因祸福避趋之。