【CF163E 】e-Government

题目

两个\(log\)的树状数组套树剖?

我们对于给出的\(n\)个模式串建立\(AC\)自动机,之后对于每一个询问串直接丢上去匹配

如果这里是暴力的话,我们直接一路跳\(fail\)累加作为结束位置还没有被删除的点就好了

我们考虑一个快点的方式,树剖

\(fail\)树建出来,直接在上面树剖维护就好了

由于只是单点修改,我们树状数组就好了

尽管是两个\(log\),但毕竟树剖和树状数组都是出了名的小常数,还是能跑过\(1e6\)

代码

#include<cstdio>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
#define re register
#define lb(x) ((x)&(-x))
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const int maxn=1e6+5;
struct E{int v,nxt;}e[maxn];
int n,m,cnt,__,num;
char S[maxn];
int c[maxn],dfn[maxn],sum[maxn],top[maxn],head[maxn];
int son[maxn][26],fa[maxn],pos[maxn],val[maxn],s[maxn];
inline void add_edge(int x,int y) {
    e[++num].v=y;e[num].nxt=head[x];head[x]=num;
}
inline void ins(int o) {
    scanf("%s",S+1);int len=strlen(S+1);
    int now=0;
    for(re int i=1;i<=len;i++) {
        if(!son[now][S[i]-'a']) son[now][S[i]-'a']=++cnt;
        now=son[now][S[i]-'a'];
    }
    pos[o]=now;
}
void dfs1(int x) {
    int maxx=-1;s[x]=-1,sum[x]=1;
    for(re int i=head[x];i;i=e[i].nxt) {
        dfs1(e[i].v);
        sum[x]+=sum[e[i].v];
        if(sum[e[i].v]>maxx) maxx=sum[e[i].v],s[x]=e[i].v;
    }
}
void dfs2(int x,int topf) {
    top[x]=topf,dfn[x]=++__;
    if(s[x]==-1) return;
    dfs2(s[x],topf);
    for(re int i=head[x];i;i=e[i].nxt)
    if(s[x]!=e[i].v) dfs2(e[i].v,e[i].v); 
}
inline void Build() {
    std::queue<int> q;
    for(re int i=0;i<26;i++) if(son[0][i]) q.push(son[0][i]);
    while(!q.empty()) {
        int k=q.front();q.pop();
        for(re int i=0;i<26;i++)
        if(son[k][i]) fa[son[k][i]]=son[fa[k]][i],q.push(son[k][i]);
            else son[k][i]=son[fa[k]][i];
    }
}
inline void add(int x,int v) {for(re int i=x;i<=cnt;i+=lb(i)) c[i]+=v;}
inline int ask(int x) {
    int now=0;
    for(re int i=x;i;i-=lb(i)) now+=c[i];
    return now;
}
inline int query(int l,int r) {return ask(r)-ask(l-1);}
inline int Query(int x) {
    int tot=0;
    while(top[x]) {
        tot+=query(dfn[top[x]],dfn[x]);
        x=fa[top[x]];
    }
    tot+=query(dfn[0],dfn[x]);
    return tot;
}
int main() {
    scanf("%d%d",&m,&n);
    for(re int i=1;i<=n;i++) ins(i);
    Build();
    for(re int i=1;i<=cnt;i++) add_edge(fa[i],i);
    dfs1(0);dfs2(0,0);cnt++;
    for(re int i=1;i<=n;i++) val[i]=1,add(dfn[pos[i]],1);
    while(m--) {
        scanf("%s",S);int len=strlen(S);
        if(S[0]=='+') {
            int x=0;
            for(re int i=1;i<len;i++) x=(x<<3)+(x<<1)+S[i]-48;
            if(val[x]) continue;
            add(dfn[pos[x]],1);val[x]=1;
        }
        if(S[0]=='-') {
            int x=0;
            for(re int i=1;i<len;i++) x=(x<<3)+(x<<1)+S[i]-48;
            if(!val[x]) continue;
            add(dfn[pos[x]],-1);val[x]=0;
        }
        if(S[0]=='?') {
            int now=0;int ans=0;
            for(re int i=1;i<len;i++) {
                now=son[now][S[i]-'a'];
                ans+=Query(now);
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/asuldb/p/10727357.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值