bzoj3881 [Coci2015]Divljak(AC自動機+fail樹+dfs序+樹狀陣列+樹鏈剖分)
阿新 • • 發佈:2019-01-04
bzoj3881 [Coci2015]Divljak
題意:
Alice有n個字串,Bob有一個字串集合T,一開始集合是空的。
接下來會發生q個操作,操作有兩種形式:
“1 P”,Bob往自己的集合裡添加了一個字串P。
“2 x”,Alice詢問Bob,集合T中有多少個字串包含串。(我們稱串A包含串B,當且僅當B是A的子串)
Bob遇到了困難,需要你的幫助。
資料範圍
1 <= n,q <= 100000
Alice和Bob擁有的字串長度之和各自都不會超過 2000000
字串都由小寫英文字母組成。
題解:
論文裡面的題解:
好題。想到對S建AC自動機就挺妙的。
A包含串B多少次,就是A在AC自動機上的每個節點,有多少在B結尾節點的fail樹子樹中。
那麼:
1操作相當於把P的這些節點染上一種新的顏色(顏色不覆蓋可並存)
2操作相當於查詢 結尾節點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;
}