码迷,mamicode.com
首页 > 其他好文 > 详细

BZOJ4231 : 回忆树

时间:2016-05-07 06:33:37      阅读:279      评论:0      收藏:0      [点我收藏+]

标签:

一个长度为$|S|$的串在树上匹配有两种情况:

1.在LCA处转弯,那么这种情况只有$O(|S|)$次,暴力提取出长度为$2|S|$的链进行KMP即可。

2.不转弯,那么可以拆成两个到根路径的询问。

对所有串的正反串建立AC自动机,求出fail树上每个点的DFS序。

然后DFS原树,记录在AC自动机上走到了哪个点,在那个点$+1$,回溯的时候$-1$。

那么一个询问的答案就是fail树上的子树和,树状数组维护即可。

时间复杂度$O(n\log n+|S|)$。

 

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100010,M=600010;
char a[M],b[N];
int n,m,len,i,j,x,y,z,g[N],v[N<<1],w[N<<1],nxt[N<<1],ed,G[N],V[N<<2],W[N<<2],NXT[N<<2];
int size[N],son[N],f[N],fs[N],d[N],top[N],loc[N],seq[N],dfn,ans[N];
inline void add(int x,int y,int z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;}
inline void ADD(int x,int y,int z){V[++ed]=y;W[ed]=z;NXT[ed]=G[x];G[x]=ed;}
void dfs(int x){
  size[x]=1;
  for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){
    f[v[i]]=x,fs[v[i]]=w[i],d[v[i]]=d[x]+1;
    dfs(v[i]),size[x]+=size[v[i]];
    if(size[v[i]]>size[son[x]])son[x]=v[i];
  }
}
void dfs2(int x,int y){
  seq[loc[x]=++dfn]=x;top[x]=y;
  if(son[x])dfs2(son[x],y);
  for(int i=g[x];i;i=nxt[i])if(v[i]!=son[x]&&v[i]!=f[x])dfs2(v[i],v[i]);
}
inline int lca(int x,int y){
  for(;top[x]!=top[y];x=f[top[x]])if(d[top[x]]<d[top[y]])swap(x,y);
  return d[x]<d[y]?x:y;
}
inline int kth(int x,int y){
  while(d[x]-d[top[x]]<y)y-=d[x]-d[top[x]]+1,x=f[top[x]];
  return seq[loc[x]-y];
}
namespace KMP{
int nxt[M];
inline void cross(int x,int y,int&ret){
  int t=0,i,j,A=kth(x,d[x]-min(d[z]+len-1,d[x])),B=kth(y,d[y]-min(d[z]+len-1,d[y]));
  for(nxt[0]=j=-1,i=1;i<len;nxt[i++]=j){
    while(~j&&a[j+1]!=a[i])j=nxt[j];
    if(a[j+1]==a[i])j++;
  }
  for(j=-1;A!=z;A=f[A]){
    while(~j&&a[j+1]!=fs[A])j=nxt[j];
    if(a[j+1]==fs[A])j++;
    if(j==len-1)ret++,j=nxt[j];
  }
  while(B!=z)b[++t]=fs[B],B=f[B];
  while(t){
    while(~j&&a[j+1]!=b[t])j=nxt[j];
    if(a[j+1]==b[t])j++;
    if(j==len-1)ret++,j=nxt[j];
    t--;
  }
}
}
namespace AC{
int tot,son[M][26],f[M],q[M],g[M],nxt[M],st[M],en[M],dfn,bit[M];
inline void addedge(int x,int y){nxt[y]=g[x];g[x]=y;}
inline int ins0(){
  int x=0;
  for(int i=0;i<len;i++){
    if(!son[x][a[i]])son[x][a[i]]=++tot;
    x=son[x][a[i]];
  }
  return x;
}
inline int ins1(){
  int x=0;
  for(int i=len-1;~i;i--){
    if(!son[x][a[i]])son[x][a[i]]=++tot;
    x=son[x][a[i]];
  }
  return x;
}
void dfs(int x){
  st[x]=++dfn;
  for(int i=g[x];i;i=nxt[i])dfs(i);
  en[x]=dfn;
}
inline void add(int x,int p){for(x=st[x];x<=dfn;x+=x&-x)bit[x]+=p;}
inline int sum(int x){int t=0;for(;x;x-=x&-x)t+=bit[x];return t;}
inline int ask(int x){return sum(en[x])-sum(st[x]-1);}
void make(){
  int h=1,t=0,i,j,x;f[0]=-1;
  for(i=0;i<26;i++)if(son[0][i])q[++t]=son[0][i];
  while(h<=t)for(x=q[h++],i=0;i<26;i++)
    if(son[x][i])f[son[x][i]]=son[f[x]][i],q[++t]=son[x][i];
    else son[x][i]=son[f[x]][i];
  for(i=1;i<=tot;i++)addedge(f[i],i);
  dfs(0);
}
}
void dfs3(int x,int y){
  AC::add(y,1);
  for(int i=G[x];i;i=NXT[i]){
    int o=W[i];
    if(o>0)ans[o]+=AC::ask(V[i]);else ans[-o]-=AC::ask(V[i]);
  }
  for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x])dfs3(v[i],AC::son[y][w[i]]);
  AC::add(y,-1);
}
int main(){
  scanf("%d%d",&n,&m);
  for(i=1;i<n;i++)scanf("%d%d%s",&x,&y,a),add(x,y,a[0]-‘a‘),add(y,x,a[0]-‘a‘);
  dfs(1),dfs2(1,1);
  for(ed=0,i=1;i<=m;i++){
    scanf("%d%d%s",&x,&y,a);len=strlen(a);z=lca(x,y);
    for(j=0;j<len;j++)a[j]-=‘a‘;
    KMP::cross(x,y,ans[i]);
    if(d[x]-d[z]>=len){
      j=AC::ins1();
      ADD(x,j,i);
      ADD(kth(x,d[x]-d[z]-len+1),j,-i);
    }
    if(d[y]-d[z]>=len){
      j=AC::ins0();
      ADD(y,j,i);
      ADD(kth(y,d[y]-d[z]-len+1),j,-i);
    }
  }
  AC::make();
  dfs3(1,0);
  for(i=1;i<=m;i++)printf("%d\n",ans[i]);
  return 0;
}

  

BZOJ4231 : 回忆树

标签:

原文地址:http://www.cnblogs.com/clrs97/p/5467637.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!