BZOJ 2434: [Noi2011]阿狸的打字机 AC自动机+fail树+线段树
Description
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。
经阿狸研究发现,这个打字机是这样工作的:
l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。
l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
例如,阿狸输入aPaPBbP,纸上被打印的字符如下:
a
aa
ab
我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?
Input
输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。
第二行包含一个整数m,表示询问个数。
接下来m行描述所有由小键盘输入的询问。其中第i行包含两个整数x, y,表示第i个询问为(x, y)。
Output
输出m行,其中第i行包含一个整数,表示第i个询问的答案。
这道题和前几天在 CF 上做的一道 G题几乎是相同的.
不知道为什么那场比赛切掉 G 题的人会那么少~
回到本题,不难发现打字机给出的其实就是 trie 的形式.
把 $trie$ 建出来后和 $fail$ 树一起跑,然后用线段树维护 dfs 序就好了.
#include <queue> #include <cstdio> #include <cstring> #include <vector> #include <algorithm> #define N 300003 #define setIO(s) freopen(s".in","r",stdin) using namespace std; struct Seg { #define lson (now<<1) #define rson (now<<1|1) struct Node { int sum; }t[N<<2]; void update(int l,int r,int now,int p,int v) { t[now].sum+=v; if(l==r) return; int mid=(l+r)>>1; if(p<=mid) update(l,mid,lson,p,v); else update(mid+1,r,rson,p,v); } int query(int l,int r,int now,int L,int R) { if(l>=L&&r<=R) return t[now].sum; int mid=(l+r)>>1,re=0; if(L<=mid) re+=query(l,mid,lson,L,R); if(R>mid) re+=query(mid+1,r,rson,L,R); return re; } #undef lson #undef rson }seg; struct Node { int ch[27],f,end; }t[N]; struct Ask { int i,y; Ask(int i=0,int y=0):i(i),y(y){} }; struct Graph { int edges; int hd[N],to[N],nex[N],fa[N],val[N]; void addedge(int u,int v,int c) { nex[++edges]=hd[u],hd[u]=edges,to[edges]=v,val[edges]=c; } }trie,tree; queue<int>q; vector<Ask>G[N]; int n,m,tot,tim,id[N],que[N],answer[N],dfn[N],size[N]; char op[N]; void buildAC() { int i,j; for(i=0;i<27;++i) if(t[0].ch[i]) q.push(t[0].ch[i]); while(!q.empty()) { int u=q.front();q.pop(); for(i=0;i<27;++i) { int p=t[u].ch[i]; if(!p) { t[u].ch[i]=t[t[u].f].ch[i]; continue; } t[p].f=t[t[u].f].ch[i]; q.push(p); } } } void dfs(int u) { dfn[u]=++tim, size[u]=1; for(int i=tree.hd[u];i;i=tree.nex[i]) dfs(tree.to[i]), size[u]+=size[tree.to[i]]; } void buildtree() { for(int i=1;i<=tot;++i) tree.addedge(t[i].f, i,0); dfs(0); } void solve(int now,int x) { seg.update(1,tim,1,dfn[now],1); for(int i=0;i<G[x].size();++i) answer[G[x][i].i]=seg.query(1,tim,1,dfn[G[x][i].y],dfn[G[x][i].y]+size[G[x][i].y]-1); for(int i=trie.hd[x];i;i=trie.nex[i]) { int v=trie.to[i],c=trie.val[i]; solve(t[now].ch[c], v); } seg.update(1,tim,1,dfn[now],-1); } int main() { int i,j,lst=0,cc=0; // setIO("input"); scanf("%s",op+1),n=strlen(op+1); for(i=1;i<=n;++i) { if(op[i]>='a'&&op[i]<='z') { int c=op[i]-'a'; if(!t[lst].ch[c]) { t[lst].ch[c]=++tot; trie.addedge(lst,tot,c); trie.fa[tot]=lst; } id[i]=lst=t[lst].ch[c]; } else { if(op[i]=='P') que[++cc]=i,id[i]=lst; if(op[i]=='B') id[i]=lst=trie.fa[lst]; } } buildAC(); buildtree(); scanf("%d",&m); for(i=1;i<=m;++i) { int x,y; scanf("%d%d",&x,&y); x=id[que[x]],y=id[que[y]]; G[y].push_back(Ask(i,x)); } solve(0,0); for(i=1;i<=m;++i) printf("%d\n",answer[i]); return 0; }