BZOJ3881 [Coci2015]Divljak題解(AC自動機+dfs序+樹鏈的並+LCA+樹上差分+樹狀陣列)
阿新 • • 發佈:2019-01-13
題目:BZOJ3881.
題目大意:Alice有n個字串
,Bob有一個字串集合T,一開始集合為空.有q個操作,操作有兩種形式:
“1 P”,Bob往自己的集合裡添加了一個字串P.
“2 x”,Alice詢問Bob,集合T中有多少個字串包含串
.
(原題太精簡了我不知道怎麼樣繼續精簡了)
首先考慮對S構建出AC自動機,那麼加入一個串後考慮T串對AC自動機上每一個點的貢獻(要麼是1要麼是0).
我們發現當T串在某位置匹配上AC自動機上的一個點時,那麼這個點在fail樹上的所有祖先都能夠被匹配上,這讓我們想到把T串放入AC自動機中暴力跳fail指標來染色,但明顯這樣做沒有效率保證會TLE.
這個時候我們新增一個概念叫做樹鏈的並,樹鏈的並就是在樹上的n條以根為一端的鏈將這些鏈之間經過的所有點都染上色.很容易想到這個東西可以暴力染色做到 ,但它其實有更優秀的 做法.
我們考慮對n條鏈按照根以外的另一端的dfs序排序,然後就可以在將n條鏈上的每一個點加1,並在相鄰兩個節點的LCA處到根這一條鏈減1,顯然這樣做是正確的,不明白的可以對著下圖yy一下:
比如像我們要染色三條鏈分別以4,5,3為另一個端點,這樣做就是很正確的(我知道這個例圖不是很優秀 ).
明白了樹鏈的並後,我們發現這樣做還是不能快速修改,所以考慮將所有資訊樹上差分一下,然後查詢就變成查詢子樹和了,樹狀陣列就可以解決.
這個時間複雜度看起來並不能過掉 級別的資料,但是由於我們用到的幾個帶log的演算法常數都很小,所以這樣做還是可以過的.
程式碼如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
#define m(a) memset(a,0,sizeof(a))
const int N=2000000,C=26;
struct Trie{
int s[C],fail;
Trie(){m(s);fail=0;}
}tr[N+9];
int cn,h[N+9];
void Trie_build(){tr[cn=0]=Trie();}
void Trie_insert(char *c,int len,int id){
int x=0;
for (int i=1;i<=len;++i)
if (tr[x].s[c[i]-'a']) x=tr[x].s[c[i]-'a'];
else {
tr[x].s[c[i]-'a']=++cn;
tr[x=cn]=Trie();
}
h[id]=x;
}
queue<int>q;
void Fail_get(){
for (int i=0;i<C;++i)
if (tr[0].s[i]) q.push(tr[0].s[i]);
while (!q.empty()){
int x=q.front(),t;q.pop();
for (int i=0;i<C;++i)
if (tr[x].s[i]) tr[tr[x].s[i]].fail=tr[tr[x].fail].s[i],q.push(tr[x].s[i]);
else tr[x].s[i]=tr[tr[x].fail].s[i];
}
}
struct tree{
int y,next;
}e[N+9];
int lin[N+9],top;
void ins(int x,int y){
e[++top].y=y;
e[top].next=lin[x];
lin[x]=top;
}
void Fail_build(){
for (int i=1;i<=cn;++i)
ins(tr[i].fail,i);
}
struct node{
int deep,fa,siz,son,top,dfn,low;
}d[N+9];
void Fail_dfs1(int k,int fa){
d[k].fa=fa;
d[k].deep=d[fa].deep+1;
d[k].siz=1;
d[k].son=cn+1;
for (int i=lin[k];i;i=e[i].next){
Fail_dfs1(e[i].y,k);
d[k].siz+=d[e[i].y].siz;
if (d[d[k].son].siz<d[e[i].y].siz) d[k].son=e[i].y;
}
}
int cdfs;
void Fail_dfs2(int k,int top){
d[k].dfn=++cdfs;
d[k].top=top;
if (d[k].son^cn+1) Fail_dfs2(d[k].son,top);
for (int i=lin[k];i;i=e[i].next)
if (e[i].y^d[k].son) Fail_dfs2(e[i].y,e[i].y);
d[k].low=cdfs;
}
int Fail_lca(int x,int y){
while (d[x].top^d[y].top)
d[d[x].top].deep>d[d[y].top].deep?x=d[d[x].top].fa:y=d[d[y].top].fa;
return d[x].deep<d[y].deep?x:y;
}
int c[N+9];
void Bit_add(int x,int v){for (;x<=cn+1;x+=x&-x) c[x]+=v;}
int Bit_query(int x){int sum=0;for (;x;x-=x&-x) sum+=c[x];return sum;}
void Fail_add(int x,int v){Bit_add(d[x].dfn,v);}
int Fail_query(int x){return Bit_query(d[x].low)-Bit_query(d[x].dfn-1);}
int tmp[N+9];
bool cmp(const int &a,const int &b){return d[a].dfn<d[b].dfn;}
void Change(char *c,int len){
int x=0;
for (int i=1;i<=len;++i){
x=tr[x].s[c[i]-'a'];
Fail_add(x,1);
tmp[i]=x;
}
sort(tmp+1,tmp+1+len,cmp);
for (int i=1;i<len;++i)
Fail_add(Fail_lca(tmp[i],tmp[i+1]),-1);
}
int n,len;
char s[N+9];
Abigail into(){
scanf("%d",&n);
Trie_build();
for (int i=1;i<=n;++i){
scanf("%s",s+1);
len=strlen(s+1);
Trie_insert(s,len,i);
}
}
Abigail work(){
Fail_get();
Fail_build();
Fail_dfs1(0,cn+1);
Fail_dfs2(0,0);
}
Abigail getans(){
int q,opt,x;
scanf("%d",&q);
for (int i=1;i<=q;++i){
scanf("%d",&opt);
if (opt==1){
scanf("%s",s+1);
len=strlen(s+1);
Change(s,len);
}else{
scanf("%d",&x);
printf("%d\n",Fail_query(h[x]));
}
}
}
int main(){
into();
work();
getans();
return 0;
}