BZOJ2434: [Noi2011]阿狸的打字机(fail树+dfs序)
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
解题思路:
长得像不像可持久化数据结构,这道题是可持久化Trie树
然而并没有强制在线a
曾经有个dalao说过:长得像可持久化+不强制在线=离线。
假如说没有问你a串在b串中出现多少次,BZOJ3172fail树裸上。
fail树中子节点个数为出现次数。
先按照打字顺序将trie树、trie图、fail树建好。
trie树要支持回溯(记录父节点)
将询问排序,按y串位置。
我们可以这样认为:
在询问版本中存在的点点权为1,不存在为0
这样统计子树大小时无需重构,更新点权即可。
再遍历原串。
遇到P
更新答案。
遇到其他:
改变一个点的点权。
当然不能暴力更新了。
询问子树权值和和更改单点点值当然要用dfs序了
代码:
1 #include<queue> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 const int N=1000000; 6 struct trnt{ 7 int ch[26]; 8 int fl; 9 int ind; 10 int oud; 11 int hd; 12 int fa; 13 }tr[N]; 14 struct ent{ 15 int twd; 16 int lst; 17 }e[N]; 18 struct qus{ 19 int asp; 20 int plc; 21 int no; 22 int ans; 23 }q[N]; 24 int lne[N]; 25 char ch[N]; 26 int fin[N]; 27 int len; 28 int pct; 29 int siz; 30 int cnt; 31 int n,m,dfn; 32 std::queue<int>Q; 33 void ade(int f,int t) 34 { 35 cnt++; 36 e[cnt].twd=t; 37 e[cnt].lst=tr[f].hd; 38 tr[f].hd=cnt; 39 } 40 bool cmp(qus x,qus y) 41 { 42 return x.plc<y.plc; 43 } 44 bool cmq(qus x,qus y) 45 { 46 return x.no<y.no; 47 } 48 int lowbit(int x) 49 { 50 return x&(-x); 51 } 52 void add(int p,int v) 53 { 54 while(p<=len&&p) 55 { 56 lne[p]+=v; 57 p+=lowbit(p); 58 } 59 return ; 60 } 61 int Val(int p) 62 { 63 int ans=0; 64 while(p) 65 { 66 ans+=lne[p]; 67 p-=lowbit(p); 68 } 69 return ans; 70 } 71 void dfs(int x) 72 { 73 tr[x].ind=++dfn; 74 for(int i=tr[x].hd;i;i=e[i].lst) 75 { 76 int to=e[i].twd; 77 dfs(to); 78 } 79 tr[x].oud=dfn; 80 return ; 81 } 82 void Build() 83 { 84 int root=0; 85 len=strlen(ch+1); 86 for(int i=1;i<=len;i++) 87 { 88 if(ch[i]=='P') 89 { 90 fin[++pct]=root; 91 }else if(ch[i]=='B') 92 { 93 root=tr[root].fa; 94 }else{ 95 int tmp=root; 96 int c=ch[i]-'a'; 97 if(!tr[root].ch[c]) 98 tr[root].ch[c]=++siz; 99 root=tr[root].ch[c]; 100 tr[root].fa=tmp; 101 } 102 } 103 root=0; 104 for(int i=0;i<26;i++) 105 if(tr[root].ch[i]) 106 Q.push(tr[root].ch[i]); 107 while(!Q.empty()) 108 { 109 root=Q.front(); 110 Q.pop(); 111 for(int i=0;i<26;i++) 112 { 113 if(tr[root].ch[i]) 114 { 115 tr[tr[root].ch[i]].fl=tr[tr[root].fl].ch[i]; 116 Q.push(tr[root].ch[i]); 117 }else 118 tr[root].ch[i]=tr[tr[root].fl].ch[i]; 119 } 120 } 121 for(int i=1;i<=siz;i++) 122 ade(tr[i].fl,i); 123 dfs(0); 124 return ; 125 } 126 void Get_ans() 127 { 128 std::sort(q+1,q+m+1,cmp); 129 int i=1; 130 int root=0; 131 int num=0; 132 for(int k=1;k<=len;k++) 133 { 134 if(ch[k]=='P') 135 { 136 num++; 137 while(q[i].plc<num&&i<=m) 138 i++; 139 while(q[i].plc==num&&i<=m) 140 { 141 int j=fin[q[i].asp]; 142 int h=Val(tr[j].oud); 143 int o=Val(tr[j].ind-1); 144 q[i].ans=h-o; 145 i++; 146 } 147 }else if(ch[k]=='B') 148 { 149 add(tr[root].ind,-1); 150 root=tr[root].fa; 151 }else{ 152 root=tr[root].ch[ch[k]-'a']; 153 add(tr[root].ind,1); 154 } 155 } 156 std::sort(q+1,q+m+1,cmq); 157 return ; 158 } 159 int main() 160 { 161 scanf("%s",ch+1); 162 scanf("%d",&m); 163 for(int i=1;i<=m;i++) 164 { 165 scanf("%d%d",&q[i].asp,&q[i].plc); 166 q[i].no=i; 167 } 168 Build(); 169 Get_ans(); 170 for(int i=1;i<=m;i++) 171 printf("%d\n",q[i].ans); 172 return 0; 173 }