1. 程式人生 > >bzoj3881 [Coci2015]Divljak(AC自動機+fail樹+dfs序+樹狀陣列+樹鏈剖分)

bzoj3881 [Coci2015]Divljak(AC自動機+fail樹+dfs序+樹狀陣列+樹鏈剖分)

bzoj3881 [Coci2015]Divljak

題意:
Alice有n個字串S1,S2...Sn,Bob有一個字串集合T,一開始集合是空的。
接下來會發生q個操作,操作有兩種形式:
“1 P”,Bob往自己的集合裡添加了一個字串P。
“2 x”,Alice詢問Bob,集合T中有多少個字串包含串Sx。(我們稱串A包含串B,當且僅當B是A的子串)
Bob遇到了困難,需要你的幫助。

資料範圍
1 <= n,q <= 100000
Alice和Bob擁有的字串長度之和各自都不會超過 2000000
字串都由小寫英文字母組成。

題解:
論文裡面的題解:
這裡寫圖片描述

好題。想到對S建AC自動機就挺妙的。
A包含串B多少次,就是A在AC自動機上的每個節點,有多少在B結尾節點的fail樹子樹中。
那麼:
   1操作相當於把P的這些節點染上一種新的顏色(顏色不覆蓋可並存)
   2操作相當於查詢

Sx結尾節點fail樹子樹顏色種類。
   
就是經典的按dfs序排序,u++,v- -,lca(u,v)- -的操作,每次查詢子樹權值和,用樹狀陣列維護。

並不需要把P也新增進AC自動機,最後還得對應到S的節點上,直接在S集合構建的AC自動機上面跑,把經過的串操作了即可。

倍增lca會被卡,寫樹鏈剖分或LCT。

程式碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace
std; const int N=2000003; queue<int> Q; int n,q,ch[N][26],fail[N],root=1,tail=1,S[N],cnt=0,C[N]; int head[N],to[N],nxt[N],num=0,pos[N],size[N],son[N],top[N],in[N],out[N],inc=0,dep[N],fa[N]; char str[N]; bool cmp(const int &A,const int &B){return in[A]<in[B];} void ADD(int x,int d) {for(int
i=x;i<=inc;i=i+(i&(-i))) C[i]+=d;} inline int query(int x){int ret=0; for(int i=x;i;i-=(i&(-i))) ret+=C[i]; return ret;} void build(int u,int v) { num++; to[num]=v; nxt[num]=head[u]; head[u]=num; } inline int insert() { int len=strlen(str); int tmp=root; for(int i=0;i<len;i++) { int c=str[i]-'a'; if(!ch[tmp][c]) ch[tmp][c]=++tail; tmp=ch[tmp][c]; } return tmp; } void getfail() { for(int i=0;i<26;i++) if(ch[root][i]) fail[ch[root][i]]=root,build(root,ch[root][i]),Q.push(ch[root][i]); else ch[root][i]=root; while(!Q.empty()) { int top=Q.front(); Q.pop(); for(int i=0;i<26;i++) { if(!ch[top][i]) ch[top][i]=ch[fail[top]][i]; else { int u=ch[top][i]; fail[u]=ch[fail[top]][i]; build(fail[u],u); Q.push(u); } } } } void dfs1(int u,int f) { size[u]=1; dep[u]=dep[f]+1; fa[u]=f; for(int i=head[u];i;i=nxt[i]) { int v=to[i]; dfs1(v,u); size[u]+=size[v]; if(size[v]>size[son[u]]) son[u]=v; } } void dfs2(int u,int tp) { top[u]=tp; inc++; in[u]=inc; if(son[u]) dfs2(son[u],tp); for(int i=head[u];i;i=nxt[i]) { int v=to[i]; if(v==son[u]) continue; dfs2(v,v); } out[u]=inc; } int getlca(int u,int v) { while(top[u]!=top[v]) { if(dep[top[u]]<dep[top[v]]) swap(u,v); u=fa[top[u]]; } return dep[u]<dep[v]?u:v; } void add() { int len=strlen(str); int tmp=root; cnt=0; for(int i=0;i<len;i++) { int c=str[i]-'a'; tmp=ch[tmp][c]; S[++cnt]=tmp; } sort(S+1,S+cnt+1,cmp); int last=0; for(int i=1;i<=cnt;i++) { if(S[i]==last) continue; ADD(in[S[i]],1); if(last!=0) ADD(in[getlca(last,S[i])],-1); last=S[i]; } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s",str); pos[i]=insert(); } getfail(); dfs1(1,1); dfs2(1,1); scanf("%d",&q); while(q--) { int opt,x; scanf("%d",&opt); if(opt==1) scanf("%s",str),add(); else { scanf("%d",&x); printf("%d\n",query(out[pos[x]])-query(in[pos[x]]-1)); } } return 0; }