bzoj3881 [Coci2015]Divljak

传送门

我好菜啊……这种题都不会做……lrd给我讲了之后我才会的……

不难想到对所有询问串建AC自动机,然后可以在每次加进来一个文本串时维护一下答案,询问的时候直接回答。

每次扔进来一个文本串的时候fail树上经过的节点到根节点的所有节点的出现次数都会+1,但注意有很多节点会被算重了,这时就需要搞一个树链的并。

首先把节点按fail树的dfs序排序,每个点的标记+1,相邻两个节点的LCA处标记-1。不难看出来这样刚好可以不重不漏覆盖树链的并的所有点,这样询问时直接子树求和即可。因为需要动态维护区间和,所以要用到树状数组。

  1 /**************************************************************
  2     Problem: 3881
  3     User: _Angel_
  4     Language: C++
  5     Result: Accepted
  6     Time:14220 ms
  7     Memory:506028 kb
  8 ****************************************************************/
  9 #include<cstdio>
 10 #include<cstring>
 11 #include<algorithm>
 12 #include<vector>
 13 using namespace std;
 14 const int maxn=100010,maxm=2000010;
 15 int insert(const char*);
 16 void getfail();
 17 void dfs(int);
 18 void match(const char*);
 19 int LCA(int,int);
 20 void add(int,int);
 21 int query(int);
 22 bool cmp(int,int);
 23 int ch[maxm][26]={{0}},f[maxm][26]={{0}},q[maxm]={0},sum[maxm]={0},cnt=0;
 24 vector<int>G[maxm];
 25 int dfn[maxm],finish[maxm],tim=0,d[maxm]={0},c[maxm]={0},a[maxm];
 26 char s[maxm];
 27 int n,m,lgn=0,iter[maxn],t,x;
 28 int main(){
 29     scanf("%d",&n);
 30     for(int i=1;i<=n;i++){
 31         scanf("%s",s);
 32         iter[i]=insert(s);
 33     }
 34     getfail();
 35     dfs(0);
 36     for(int j=1;j<=lgn;j++)for(int i=1;i<=cnt;i++)f[i][j]=f[f[i][j-1]][j-1];
 37     scanf("%d",&m);
 38     while(m--){
 39         scanf("%d",&t);
 40         if(t==1){
 41             scanf("%s",s);
 42             match(s);
 43         }
 44         else{
 45             scanf("%d",&x);
 46             x=iter[x];
 47             printf("%d\n",query(finish[x])-query(dfn[x]-1));
 48         }
 49     }
 50     return 0;
 51 }
 52 int insert(const char *c){
 53     int x=0;
 54     while(*c){
 55         if(!ch[x][*c-'a'])ch[x][*c-'a']=++cnt;
 56         x=ch[x][*c++-'a'];
 57     }
 58     return x;
 59 }
 60 void getfail(){
 61     int x,head=0,tail=0;
 62     for(int c=0;c<26;c++)if(ch[0][c])q[tail++]=ch[0][c];
 63     while(head!=tail){
 64         x=q[head++];//printf("x=%d fail=%d\n",x,f[x][0]);
 65         G[f[x][0]].push_back(x);
 66         fill(f[x]+1,f[x]+26,cnt+1);
 67         for(int c=0;c<26;c++){
 68             if(ch[x][c]){
 69                 int y=f[x][0];
 70                 while(y&&!ch[y][c])y=f[y][0];
 71                 f[ch[x][c]][0]=ch[y][c];
 72                 q[tail++]=ch[x][c];
 73             }
 74             else ch[x][c]=ch[f[x][0]][c];
 75         }
 76     }
 77     fill(f[0],f[0]+26,cnt+1);
 78 }
 79 void dfs(int x){//printf("dfs(%d)\n",x);
 80     dfn[x]=++tim;
 81     d[x]=d[f[x][0]]+1;
 82     while((1<<lgn)<d[x])lgn++;
 83     for(int i=0;i<(int)G[x].size();i++)dfs(G[x][i]);
 84     finish[x]=tim;
 85 }
 86 void match(const char *c){
 87     int x=0;
 88     a[0]=0;
 89     while(*c){
 90         x=ch[x][*c++-'a'];
 91         a[++a[0]]=x;
 92     }
 93     sort(a+1,a+a[0]+1,cmp);
 94     add(dfn[a[1]],1);
 95     for(int i=2;i<=a[0];i++){
 96         add(dfn[LCA(a[i],a[i-1])],-1);
 97         add(dfn[a[i]],1);
 98     }
 99 }
100 int LCA(int x,int y){//printf("LCA(%d,%d)=",x,y);
101     if(d[x]!=d[y]){
102         if(d[x]<d[y])swap(x,y);
103         for(int i=lgn;i>=0;i--)if(d[f[x][i]]>=d[y])x=f[x][i];
104     }
105     if(x==y)return x;
106     for(int i=lgn;i>=0;i--)if(f[x][i]!=f[y][i]){
107         x=f[x][i];
108         y=f[y][i];
109     }
110     return f[x][0];
111 }
112 void add(int x,int d){
113     while(x<=tim){
114         c[x]+=d;
115         x+=x&-x;
116     }
117 }
118 int query(int x){
119     int ans=0;
120     while(x){
121         ans+=c[x];
122         x&=x-1;
123     }
124     return ans;
125 }
126 bool cmp(int x,int y){return dfn[x]<dfn[y];}
View Code

 

posted @ 2017-03-14 21:04  AntiLeaf  阅读(161)  评论(0编辑  收藏  举报