洛谷P5840 [COCI2015]Divljak (AC自动机+fail树上dfs序+树上差分线段树维护)

题目链接:https://www.luogu.com.cn/problem/P5840

解法:如题所示,不得不承认是一道非常好的题,就是有点麻烦qwq。首先考虑用S建AC自动机,然后对每一个点建fail[i]->i的dfs树,然后把每次添加的T串丢到AC自动机上跑。每次跑的时候记录T串每个位置在AC自动机里的位置。排序后(因为如果不排序的话,这些点就杂乱无章,就无法有效地进行树上差分,我们要的是每次T串丢进来看该点有没有使用过而已,所以如果你学过虚树会有所了解,用树上差分就是2个点+1,其LCA-1,为什么是-1而不是-2呢?因为我们需要记录该点有没有使用过,使用过的话就一直是1,最后维护的线段树其实就是查询的点包括其子树一共有几个点,因为根据fail指针的性质我们可以知道,一旦他儿子有匹配到,那他自己必有匹配。大概就是这样子吧,具体看代码,感觉啰里啰嗦自己还说不清。

AC代码:

#include<bits/stdc++.h>
#pragma GCC optimize(2)
#define ll long long
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
#define endl '\n'
#define eps 0.000000001
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a))
#define IO ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
const int INF=0x3f3f3f3f;
const ll inf=0x3f3f3f3f3f3f3f3f;
const int mod=1e9+7;
const int maxn=2e6+5;
int tot,head[maxn];
struct E{
    int to,next;
}edge[maxn<<1];
void add(int u,int v){
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
class ac_auto{
    public:
        const static int maxn=2e6+100;
        int tot,root;
        int tree[maxn][30]; 
        int pos[maxn];
        int fail[maxn];int ans[1005];
        int newnode(){
            for(int i=0;i<26;i++){
                tree[tot][i]=0;
            }
            pos[tot]=0;
            return tot++;
        }
        void init(){
            mem(ans,0);tot=0;
            root=newnode();
        }                
        void insert_(string s,int t){
            int now = root;
            for(int i=0;i<s.length();i++){
                int id=s[i]-'a';
                if(!tree[now][id]){
                    tree[now][id]=newnode();
                }
                now=tree[now][id];
            }
            pos[t]=now;
        }
        
        void getFail(){
            queue <int>q;
            for(int i=0;i<26;i++){
                if(tree[0][i]){
                    fail[tree[0][i]] = 0;
                    q.push(tree[0][i]);
                }
            }
            while(!q.empty()){
                int now = q.front();
                q.pop();
                for(int i=0;i<26;i++){
                    if(tree[now][i]){
                        fail[tree[now][i]] = tree[fail[now]][i];
                        q.push(tree[now][i]);
                    }
                    else
                        tree[now][i] = tree[fail[now]][i];
                }
            }
        }    
}acam;
int fa[maxn],dep[maxn],siz[maxn],son[maxn];
void dfs1(int u,int f){
    fa[u]=f;
    dep[u]=dep[f]+1;
    siz[u]=1;
    int maxsize=-1;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        if(v==f) continue;
        dfs1(v,u);
        siz[u]+=siz[v];
        if(siz[v]>maxsize){
            maxsize=siz[v];
            son[u]=v;
        }
    }
}
int tim,dfn[maxn],top[maxn];
void dfs2(int u,int t){
    dfn[u]=++tim;
    top[u]=t;
    if(!son[u]) return ;
    dfs2(son[u],t);
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}
int LCA(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) std::swap(x,y);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) std::swap(x,y);
    return x;
}
class segment_tree{
    public:
        struct node{
            int l,r;
            int sum;
        }tree[10000010];
        
        inline void build(int i,int l,int r){
            tree[i].l=l;tree[i].r=r;
            if(l==r){
                tree[i].sum=0;
                return ;
            }
            int mid=(l+r)>>1;
            build(i*2,l,mid);
            build(i*2+1,mid+1,r);
            tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
        }
        
        inline int search(int i,int l,int r){
            if(tree[i].l>=l && tree[i].r<=r)
                return tree[i].sum;
            if(tree[i].r<l || tree[i].l>r)  return 0;
            int s=0;
            if(tree[i*2].r>=l)  s+=search(i*2,l,r);
            if(tree[i*2+1].l<=r)  s+=search(i*2+1,l,r);
            return s;
        }
        
        inline void update(int i,int dis,int k){
            if(tree[i].l==tree[i].r){
                tree[i].sum+=k;
                return ;
            }
            if(dis<=tree[i*2].r)  update(i*2,dis,k);
            else  update(i*2+1,dis,k);
            tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
            return ;
        }        
}st;
bool cmp(int a,int b){
    return dfn[a]<dfn[b];
}
int a[maxn];
void solve1(string s){
    int now=acam.root,tp=0;
    for(int i=0;i<s.length();i++){
        now = acam.tree[now][s[i]-'a'];
        a[++tp]=now;
    }
    sort(a+1,a+tp+1,cmp);
    bool flag=false;
    rep(i,1,tp){
        st.update(1,dfn[a[i]],1);
        if(flag){
            st.update(1,dfn[LCA(a[i],a[i-1])],-1);
        }else{
            flag=true;
        }
    }
}
int solve2(int x){
    return st.search(1,1,dfn[acam.pos[x]]+siz[acam.pos[x]]-1)-st.search(1,1,dfn[acam.pos[x]]-1);
}
int main(){
    int n;scanf("%d",&n);mem(head,-1);
    acam.init();
    rep(i,1,n){
        string s;cin>>s;
        acam.insert_(s,i);
    }
    acam.getFail();
    rep(i,1,acam.tot-1){
        add(acam.fail[i],i);
    }
    dfs1(0,acam.tot);
    dfs2(0,0);
    st.build(1,1,tim);
    int q;scanf("%d",&q);
    while(q--){
        int op;scanf("%d",&op);
        if(op==1){
            string s;cin>>s;
            solve1(s);
        }else{
            int x;scanf("%d",&x);
            printf("%d\n",solve2(x));
        }
    }
}
View Code

 

posted @ 2020-08-05 20:28  Anonytt  阅读(165)  评论(0编辑  收藏  举报