2434: [Noi2011]阿狸的打字机
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2776 Solved: 1547
[Submit][Status][Discuss]
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个询问的答案。
Sample Input
3
1 2
1 3
2 3
Sample Output
1
0
HINT
1<=N<=10^5
Source
分析:
很显然的一点就是我们把fail树拎出来,然后y串在x的子树中的出现节点数就是x在y中的出现次数...
但是显然我们不能直接在线修改查询,问题就在于修改次数过多...
所以可以考虑离线,处理出fail树的dfs序,用树状数组维护区间和,把第i个串的节点处设为1,然后查询子树区间和...
由于这道题的输入的特殊性,所以做到$O(nlgn)$...
我还是太弱了...只能想到fail树...不能想到dfs序维护区间和...
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> //by NeighThorn using namespace std; const int maxn=100000+5; int n,cnt,dfn,tot,head,tail,q[maxn],fa[maxn],be[maxn],en[maxn],hd[maxn],to[maxn],ans[maxn],nxt[maxn],node[maxn],tree[maxn]; char s[maxn]; struct trie{ int fail,nxt[26]; }tr[maxn]; struct M{ int x,y,id; friend bool operator < (M a,M b){ return a.y<b.y; } }query[maxn]; inline void add(int x,int y){ to[cnt]=y;nxt[cnt]=hd[x];hd[x]=cnt++; } inline void build(void){ int p=0;tr[0].fail=-1; for(int i=0;s[i];i++){ if(s[i]=='B') p=fa[p]; else if(s[i]=='P') node[++cnt]=p; else{ if(!tr[p].nxt[s[i]-'a']) tr[p].nxt[s[i]-'a']=++tot,tr[tot].fail=-1; fa[tr[p].nxt[s[i]-'a']]=p; p=tr[p].nxt[s[i]-'a']; } } } inline void buildACM(void){ head=0,tail=0,q[0]=0; while(head<=tail){ int id=q[head++],p=-1; for(int i=0;i<26;i++) if(tr[id].nxt[i]){ if(id){ p=tr[id].fail; while(p!=-1){ if(tr[p].nxt[i]){ tr[tr[id].nxt[i]].fail=tr[p].nxt[i]; break; } p=tr[p].fail; } if(p==-1) tr[tr[id].nxt[i]].fail=0; } else tr[tr[id].nxt[i]].fail=0; q[++tail]=tr[id].nxt[i]; } } } inline void dfs(int root){ be[root]=++dfn; for(int i=hd[root];i!=-1;i=nxt[i]) dfs(to[i]); en[root]=dfn; } inline void insert(int x,int y){ for(;x<=dfn;x+=x&-x) tree[x]+=y; } inline int Query(int x){ int res=0; for(;x;x-=x&-x) res+=tree[x]; return res; } inline void solve(void){ int p=0,j=1,lala=0; for(int i=0;s[i];i++){ if(s[i]=='B') insert(be[p],-1),p=fa[p]; else if(s[i]=='P'){ lala++; while(query[j].y==lala) ans[query[j].id]=Query(en[node[query[j].x]])-Query(be[node[query[j].x]]-1),j++; } else p=tr[p].nxt[s[i]-'a'],insert(be[p],1); } } signed main(void){ memset(hd,-1,sizeof(hd)); scanf("%s%d",s,&n);dfn=tot=cnt=0; build();buildACM();cnt=0; for(int i=1;i<=tot;i++) add(tr[i].fail,i); dfs(0); for(int i=1;i<=n;i++) scanf("%d%d",&query[i].x,&query[i].y),query[i].id=i; sort(query+1,query+n+1);solve(); for(int i=1;i<=n;i++) printf("%d ",ans[i]); return 0; }
By NeighThorn