BZOJ 2434 NOI2011 阿狸的打字机 fail树+树状数组

来源:互联网 发布:毫州康美中药城淘宝店 编辑:程序博客网 时间:2024/09/21 06:32

题目大意:初始字串为空,首先给定一系列操作序列,有三种操作:

1.在结尾加一个字符

2.在结尾删除一个字符

3.打印当前字串

然后多次询问第x个打印的字串在第y个打印的字串中出现了几次

卡了很久……到底还是对AC自动机了解不是很深啊QAQ

fail树不是很难想 至少在用AC自动机切掉3172之后不是很难想……

首先构建AC自动机 注意由于这个字串的特殊构造 我们不必每打印一个字符串再插入Trie树

令now为当前指针 初始为root 考虑三种操作

在结尾添加一个字符->新建一个子节点(若存在在不用新建),进入该子节点

在结尾删除一个字符->返回到父亲节点

打印当前字串->在当前节点标记是第几个字符串

那么查询x在y中出现了几次 就是查询y有多少个节点沿着fail指针能找到x (AC自动机基本操作)

那么我们反向思考 查询y有多少个节点沿着fail指针能找到x 就是查询x沿着反向的fail指针能找到多少个y的节点

fail指针没有环 每个节点只有一个出度 那么反向之后显然是一棵树 x沿着反向的fail指针所能到达的节点就是x所在的子树

于是我们可以沿着反向的fail指针搞出DFS序 x所在的子树就是DFS中对应的区间 我们要查询的是x对应的区间中有多少个y

想高级数据结构的可以洗洗睡了

我们可以这么搞

对于每个y:

我们把关于y的询问都存在邻接表里 然后把y所有的节点在DFS序中的位置插入树状数组,然后对于关于y的每个询问在树状数组上查询一遍即可。

那么问题来了:这不会TLE么?

答案是不会的 因为这题的特殊性 所以树状数组可以达到O(nlogn)

求出DFS序之后 我们回来考虑这些操作序列

在结尾添加一个字符->添加的字符所在节点加入树状数组

在结尾删除一个字符->删除的字符所在节点从树状数组删除

打印当前字串-> 处理询问

然后就搞出来了 1A我也是醉了

#include <vector>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define M 100100using namespace std;struct Trie{Trie *son[26],*fail;vector<Trie*> next;int ed,pos;void* operator new (size_t);}*root=new Trie,mempool[M],*C=mempool;struct abcd{int to,pos,next;}table[M];int head[M],tot;int n,m,ans[M];char s[M];int start[M],end[M],cnt;int c[M];void* Trie :: operator new (size_t){return C++;}void Add(int x,int y,int z){table[++tot].to=y;table[tot].pos=z;table[tot].next=head[x];head[x]=tot;}void Update(int x,int y){for(;x<=cnt;x+=x&-x)c[x]+=y;}int Get_Ans(int x){int re=0;for(;x;x-=x&-x)re+=c[x];return re;}void Build_Tree(){int i;static Trie* stack[M];static int top;stack[++top]=root;for(i=1;s[i];i++){switch(s[i]){case 'B':stack[top--]=0x0;break;case 'P':stack[top]->ed=++n;break;default:if(!stack[top]->son[s[i]-'a'])stack[top]->son[s[i]-'a']=new Trie;stack[++top]=stack[top-1]->son[s[i]-'a'];break;}}static Trie *q[M];static int r,h;for(i=0;i<26;i++)if(root->son[i]){root->son[i]->fail=root;root->next.push_back(q[++r]=root->son[i]);}while(r!=h){Trie *p=q[++h];for(i=0;i<26;i++)if(p->son[i]){Trie *temp=p->fail;while(temp!=root&&!temp->son[i])temp=temp->fail;if(temp->son[i])temp=temp->son[i];p->son[i]->fail=temp;temp->next.push_back(q[++r]=p->son[i]);}}}void DFS(Trie *p){vector<Trie*>::iterator it;p->pos=++cnt;if(p->ed) start[p->ed]=cnt; for(it=p->next.begin();it!=p->next.end();it++)DFS(*it);if(p->ed) end[p->ed]=cnt;}void Solve(){int i,j;static Trie* stack[M];static int top;stack[++top]=root;for(j=1;s[j];j++){switch(s[j]){case 'B':Update(stack[top]->pos,-1);stack[top--]=0x0;break;case 'P':for(i=head[stack[top]->ed];i;i=table[i].next)ans[table[i].pos]=Get_Ans(end[table[i].to])-Get_Ans(start[table[i].to]-1);break;default:stack[++top]=stack[top-1]->son[s[j]-'a'];Update(stack[top]->pos,1);break;}}}int main(){int i,x,y;scanf("%s",s+1);Build_Tree();DFS(root);cin>>m;for(i=1;i<=m;i++){scanf("%d%d",&x,&y);Add(y,x,i);}Solve();for(i=1;i<=m;i++)printf("%d\n",ans[i]);}


0 0