首页 > 代码库 > BZOJ 2434 NOI2011 阿狸的打字机 fail树+树状数组
BZOJ 2434 NOI2011 阿狸的打字机 fail树+树状数组
题目大意:初始字串为空,首先给定一系列操作序列,有三种操作:
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 100100 using 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]); }
BZOJ 2434 NOI2011 阿狸的打字机 fail树+树状数组