BZOJ3881 [Coci2015]Divljak题解(AC自动机+dfs序+树状数组+树链的并)

题目:BZOJ3881.
题目大意:Alice有 n n n个字符串 S 1 , S 2 . . . S n S_1,S_2...S_n S1,S2...Sn,Bob有一个字符串集合 T T T,一开始集合为空.有 q q q个操作,操作有两种形式:
1   P 1\,P 1P,Bob往自己的集合里添加了一个字符串 P P P.
2   x 2\,x 2x,Alice询问Bob,集合 T T T中有多少个字符串包含串 S x S_x Sx.
(原题太精简了我不知道怎么样继续精简了)
1 ≤ n , q ≤ 1 0 5 , 1 ≤ ∑ ∣ S i ∣ , ∑ ∣ T i ∣ ≤ 2 ∗ 1 0 6 1\leq n,q\leq 10^5,1\leq \sum |S_i|,\sum |T_i|\leq 2*10^6 1n,q105,1Si,Ti2106.

首先考虑对 S S S构建出AC自动机,那么加入一个串后考虑 T T T串对AC自动机上每一个点的贡献(要么是 1 1 1要么是 0 0 0).

我们发现当 T T T串在某位置匹配上AC自动机上的一个点时,那么这个点在fail树上的所有祖先都能够被匹配上,这让我们想到把 T T T串放入AC自动机中暴力跳fail指针来染色,但明显这样做没有效率保证会TLE.

很容易发现这个做法的复杂度比较假(虽说可以证明是 O ( n n ) O(n\sqrt{n}) O(nn ),证明类似BZOJ3277),但其实这个问题就是树链的并,直接用dfs序+树状数组维护一下树链的并就可以 O ( n log ⁡ n ) O(n\log n) O(nlogn)了.

其实用线段树合并也可以 O ( n log ⁡ n ) O(n\log n) O(nlogn).

这个时间复杂度看起来并不能过掉 2 ∗ 1 0 6 2*10^6 2106级别的数据,但是由于我们用到的几个带 log ⁡ \log 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;
}tr[N+9];
int cn,h[N+9];

void Trie_build(){cn=0;}

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;
      x=cn;
    }
  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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值