BZOJ 3881 [COCI2015]Divljak(AC自动机+LCA+BIT)

10 篇文章 0 订阅
5 篇文章 0 订阅

题目描述:
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. 1.P Bob往自己的集合里添加了一个字符串P
2. 2 x Alice询问Bob,集合T中有多少个字符串包含串sx,(我们称串A包含串B,当且仅当B是A的子串)

题解:
用n个字符串 S 1 , S 2 , . . . S n S_1,S_2,...S_n S1,S2,...Sn建立AC自动机,扒出fail树,然后每添加一个P串,就把它拿到AC自动机上跑一遍,把到达的结点都记录下来,由于对每个串都只贡献一次,所以这一次的贡献就是这些点到根节点的树链的并。

然后求树链的并,我们只需要将所有结点按照DFS序排序,把每个点的权值+1,相邻结点的LCA的权值-1即可。

最后用树状数组单点修改+计算子树和完成收尾工作


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#include<ext/rope>
using namespace std;
using namespace __gnu_cxx;
#define LL long long
#define pii pair<int,int>
#define mp(a,b) make_pair(a,b)
const int MAXN = 2e6+10;
const int MOD = 1000000007;
const int INF = 0x3f3f3f3f;
int dep[MAXN],f[MAXN][24],dfn[MAXN],sz[MAXN],head[MAXN];
int nxt[MAXN][26],fail[MAXN],id[MAXN],a[MAXN],bit[MAXN];
int tot,cnt,atot,n; char s[MAXN];
struct node{ int v,nxt; }edge[MAXN<<1];
inline void add_edge(int u,int v){
    edge[++atot].v=v; edge[atot].nxt=head[u]; head[u]=atot;
}
inline void Insert(char *s,int k){
    int len=strlen(s),rt=0;
    for(int i=0;i<len;i++){
        if(!nxt[rt][s[i]-'a']) nxt[rt][s[i]-'a']=++tot;
        rt = nxt[rt][s[i]-'a'];
    }
    id[k]=rt;
}
inline void Build(){
    queue<int> que;
    for(int i=0;i<26;i++) if(nxt[0][i]) que.push(nxt[0][i]),add_edge(0,nxt[0][i]);
    while(!que.empty()){
        int u=que.front(); que.pop();
        for(int i=0;i<26;i++){
            if(nxt[u][i]){
                fail[nxt[u][i]]=nxt[fail[u]][i];
                que.push(nxt[u][i]);
                add_edge(fail[nxt[u][i]],nxt[u][i]);
            }else{
                nxt[u][i]=nxt[fail[u]][i];
            }
        }
    }
}
void dfs(int u,int fa){
    f[u][0]=fa; dep[u]=dep[fa]+1; dfn[u]=++cnt; sz[u]=1;
    for(int i=head[u];i;i=edge[i].nxt){
        int v = edge[i].v;
        if(v == fa) continue;
        dfs(v,u);
        sz[u] += sz[v];
    }
}
inline void Init(){
    for(int j=1;j<=21;j++)
        for(int i=1;i<=cnt;i++)
            f[i][j]=f[f[i][j-1]][j-1];
}
inline int LCA(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=21;i>=0;i--)
        if(dep[f[x][i]]>=dep[y])
            x=f[x][i];
    if(x==y) return x;
    for(int i=21;i>=0;i--)
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    return f[x][0];
}
inline int lowbit(int x){ return x&-x; }
inline void add(int x,int v){ for(int i=x;i<=cnt;i+=lowbit(i)) bit[i]+=v; }
inline int query(int x,int res=0){ for(int i=x;i;i-=lowbit(i)) res+=bit[i]; return res; }
inline bool cmp(int x,int y){ return dfn[x]<dfn[y]; }
inline void update(char *s){
    int rt=0,len=strlen(s),m=0;
    a[m++]=0;
    for(int i=0;i<len;i++) rt=nxt[rt][s[i]-'a'],a[m++]=rt;
    sort(a,a+m,cmp);
    int k = unique(a,a+m)-a;
    for(int i=0;i<k;i++){
        add(dfn[a[i]],1);
        if(i) add(dfn[LCA(a[i-1],a[i])],-1);
    }
}
signed main(){
#ifndef ONLINE_JUDGE
    freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
#endif // ONLINE_JUDGE
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%s",s),Insert(s,i);
    Build(); dfs(0,0); Init();
    int Q; scanf("%d",&Q);
    while(Q--){
        int op; scanf("%d",&op);
        if(op==1) scanf("%s",s),update(s);
        else{
            int x; scanf("%d",&x);
            int l=dfn[id[x]],r=dfn[id[x]]+sz[id[x]]-1;
            printf("%d\n",query(r)-query(l-1));
        }
    }
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值