BZOJ4231回忆树 离线+fail树+KMP
Posted CQzhangyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ4231回忆树 离线+fail树+KMP相关的知识,希望对你有一定的参考价值。
【BZOJ4231】回忆树
Description
回忆树是树。
具体来说,是n个点n-1条边的无向连通图,点标号为1~n,每条边上有一个字符(出于简化目的,我们认为只有小写字母)。
对一棵回忆树来说,回忆当然是少不了的。
一次回忆是这样的:你想起过往,触及心底…唔,不对,我们要说题目。
这题中我们认为回忆是这样的:给定2个点u,v(u可能等于v)和一个非空字符串s,问从u到v的简单路径上的所有边按照到u的距离从小到大的顺序排列后,边上的字符依次拼接形成的字符串中给定的串s出现了多少次。
Input
第一行2个整数,依次为树中点的个数n和回忆的次数m。
接下来n-1行,每行2个整数u、v和1个小写字母c,表示回忆树的点u、v之间有一条边,边上的字符为c
接下来2m行表示m次回忆,每次回忆2行:第1行2个整数u、v,第2行给出回忆的字符串s。
Output
对于每次回忆,输出串s出现的次数。
Sample Input
12 3
1 2 w
2 3 w
3 4 x
4 5 w
5 6 w
6 7 x
7 8 w
8 9 w
9 10 x
10 11 w
11 12 w
1 7
wwx
1 12
www
1 12
w
1 2 w
2 3 w
3 4 x
4 5 w
5 6 w
6 7 x
7 8 w
8 9 w
9 10 x
10 11 w
11 12 w
1 7
wwx
1 12
www
1 12
w
Sample Output
2
0
8
0
8
HINT
对于100%的数据,n<=100000,m<=100000,询问串的总长<=300000
题解:一开始想反了,以为要把原树建成AC自动机。。。不可做?
我们将询问分成两段,一段上去的和一段下去的,这样只需要对询问串的正串和反串分别维护AC自动机即可。那么中间的呢?由于总长不超过2K,所以暴力拿出来跑KMP即可。
考虑每一段,我们采用离线的方法,将询问挂链到树的节点上,在上端的点系数为-1,下端的点系数为+1,然后DFS一遍整棵树,每扫到一个点就将这个点在AC自动机中对应点的权值+1。然后考虑在这个点挂链的所有询问,用询问串对应点在fail树中子树的点权和更新答案即可。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <queue> using namespace std; const int maxn=100010; int n,m,t1,t2,l1,l2,cnt; queue<int> Q; struct AC { int tot; int ch[26],fail; }a1[maxn*3],a2[maxn*3]; struct Fail_Tree { int tot; AC *a; vector<int> ch[maxn*3]; int p[maxn*3],q[maxn*3],s[maxn*3]; void dfs(int x) { p[x]=++p[0]; for(int i=0;i<(int)ch[x].size();i++) dfs(ch[x][i]); q[x]=p[0]; } void build() { Q.push(1); int i,u; while(!Q.empty()) { u=Q.front(),Q.pop(); for(i=0;i<26;i++) { if(u==1) { if(a[u].ch[i]) Q.push(a[u].ch[i]),a[a[u].ch[i]].fail=1; else a[u].ch[i]=1; continue; } if(!a[u].ch[i]) { a[u].ch[i]=a[a[u].fail].ch[i]; continue; } Q.push(a[u].ch[i]); a[a[u].ch[i]].fail=a[a[u].fail].ch[i]; } } for(i=2;i<=tot;i++) ch[a[i].fail].push_back(i); dfs(1); } inline void updata(int x,int v) { for(int i=x;i<=tot;i+=i&-i) s[i]+=v; } inline int query(int x) { int i,ret=0; for(i=x;i;i-=i&-i) ret+=s[i]; return ret; } }f1,f2; struct QUERY { int a,b,u,v,top,ans; }q[maxn]; struct node { int org,k; node() {} node(int a,int b) {org=a,k=b;} }; vector<node> b1[maxn],b2[maxn]; int val[maxn<<1],next[maxn<<1],to[maxn<<1],head[maxn],fa[20][maxn],Log[maxn],nxt[maxn*3],from[maxn],dep[maxn]; char T[maxn*3],S[maxn]; inline void add(int a,int b,int c) { to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++; } void getfa(int x) { for(int i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[0][x]) fa[0][to[i]]=x,dep[to[i]]=dep[x]+1,from[to[i]]=val[i],getfa(to[i]); } inline int lca(int a,int b) { if(dep[a]<dep[b]) swap(a,b); int i; for(i=Log[dep[a]-dep[b]];i>=0;i--) if(dep[fa[i][a]]>=dep[b]) a=fa[i][a]; if(a==b) return a; for(i=Log[dep[a]];i>=0;i--) if(fa[i][a]!=fa[i][b]) a=fa[i][a],b=fa[i][b]; return fa[0][a]; } inline int FA(int x,int y) { for(int i=Log[y];i>=0;i--) if((1<<i)<=y) y-=(1<<i),x=fa[i][x]; return x; } inline int KMP() { int i,j,ret=0; nxt[0]=-1,i=0,j=-1; while(i<l2) { if(j==-1||T[i]==T[j]) nxt[++i]=++j; else j=nxt[j]; } i=j=0; while(i<l1) { if(j==-1||S[i]==T[j]) i++,j++; else j=nxt[j]; if(j==l2) ret++; } return ret; } void dfs(int x,int r1,int r2) { f1.updata(f1.p[r1],1),f2.updata(f2.p[r2],1); int i,a; for(i=0;i<(int)b1[x].size();i++) { a=b1[x][i].org; q[a].ans+=b1[x][i].k*(f1.query(f1.q[q[a].a])-f1.query(f1.p[q[a].a]-1)); } for(i=0;i<(int)b2[x].size();i++) { a=b2[x][i].org; q[a].ans+=b2[x][i].k*(f2.query(f2.q[q[a].b])-f2.query(f2.p[q[a].b]-1)); } for(i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[0][x]) dfs(to[i],a1[r1].ch[val[i]],a2[r2].ch[val[i]]); f1.updata(f1.p[r1],-1),f2.updata(f2.p[r2],-1); } inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘) f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+gc-‘0‘,gc=getchar(); return ret*f; } int main() { n=rd(),m=rd(); int i,j,a,b,u; memset(head,-1,sizeof(head)); for(i=1;i<n;i++) a=rd(),b=rd(),scanf("%s",T),add(a,b,T[0]-‘a‘),add(b,a,T[0]-‘a‘); dep[1]=1,getfa(1); for(i=2;i<=n;i++) Log[i]=Log[i>>1]+1; for(j=1;(1<<j)<=n;j++) for(i=1;i<=n;i++) fa[j][i]=fa[j-1][fa[j-1][i]]; t1=t2=1; for(i=1;i<=m;i++) { q[i].u=rd(),q[i].v=rd(),q[i].top=lca(q[i].u,q[i].v); scanf("%s",T),l2=strlen(T); for(u=1,j=0;j<l2;j++) { b=T[j]-‘a‘; if(!a1[u].ch[b]) a1[u].ch[b]=++t1; u=a1[u].ch[b]; } q[i].a=u; for(u=1,j=l2-1;j>=0;j--) { b=T[j]-‘a‘; if(!a2[u].ch[b]) a2[u].ch[b]=++t2; u=a2[u].ch[b]; } q[i].b=u; if(q[i].top!=q[i].u&&q[i].top!=q[i].v) { b=min(l2-1,dep[q[i].u]-dep[q[i].top]),l1=b; for(a=0,j=FA(q[i].u,dep[q[i].u]-dep[q[i].top]-b);j!=q[i].top;a++,j=fa[0][j]) S[a]=from[j]+‘a‘; b=min(l2-1,dep[q[i].v]-dep[q[i].top]),l1+=b; for(a=l1-1,j=FA(q[i].v,dep[q[i].v]-dep[q[i].top]-b);j!=q[i].top;a--,j=fa[0][j]) S[a]=from[j]+‘a‘; q[i].ans+=KMP(); } if(dep[q[i].v]-dep[q[i].top]>=l2) b1[q[i].v].push_back(node(i,1)),b1[FA(q[i].v,dep[q[i].v]-dep[q[i].top]-l2+1)].push_back(node(i,-1)); if(dep[q[i].u]-dep[q[i].top]>=l2) b2[q[i].u].push_back(node(i,1)),b2[FA(q[i].u,dep[q[i].u]-dep[q[i].top]-l2+1)].push_back(node(i,-1)); } f1.a=a1,f2.a=a2,f1.tot=t1,f2.tot=t2; f1.build(),f2.build(); dfs(1,1,1); for(i=1;i<=m;i++) printf("%d\n",q[i].ans); return 0; }//12 3 1 2 w 2 3 w 3 4 x 4 5 w 5 6 w 6 7 x 7 8 w 8 9 w 9 10 x 10 11 w 11 12 w 1 7 wwx 1 12 www 1 12 w //8 1 1 2 a 2 3 b 3 4 a 1 5 b 5 6 a 6 7 b 7 8 a 3 7 ab
以上是关于BZOJ4231回忆树 离线+fail树+KMP的主要内容,如果未能解决你的问题,请参考以下文章
bzoj 2434 [Noi2011]阿狸的打字机(fail树+离线处理+BIT)
BZOJ2434-[Noi2011]阿狸的打字机(AC自动机(fail树)+离线+树状数组)