【BZOJ4231】回忆树 离线+fail树+KMP
生活随笔
收集整理的這篇文章主要介紹了
【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 31 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
20
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?
轉載于:https://www.cnblogs.com/CQzhangyu/p/7749512.html
總結
以上是生活随笔為你收集整理的【BZOJ4231】回忆树 离线+fail树+KMP的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: P1799 数列_NOI导刊2010提高
- 下一篇: 苹果过审ipv6问题