【经典】虚树+字典树——ICPC NCNA 2018 A

首先建立好trie,由于问题询问的结点和不超过2e5,可以用虚树解决

bug百出。。调试了一上午

/*
对给定串建立trie,标记上终止结点,d[u]表示结点u的深度 
对于每个询问,标记k个串对应的终止结点,然后建立虚树
虚树里维护size[u],fa[u],所有size[u]=L的点都是符合要求的点,其贡献为
    -d[fa[u]]+d[u]; 
*/

#include<bits/stdc++.h>
using namespace std;
#define N 400005

int n,Q,K,L;
int id[N];//第i个串对应的终止结点 

int d[N],f[N][25],dfn[N],vis[N];//trie树上结点深度,倍增数组
struct trie{
    int nxt[N][26],root,L;
    int newnode(){
        ++L;
        memset(nxt[L],-1,sizeof nxt[L]);
        return L;
    }
    void init(){
        L=0;
        root=newnode();
    }
    void insert(char buf[],int idd){
        int len=strlen(buf);
        int now=root;
        for(int i=0;i<len;i++){
            if(nxt[now][buf[i]-'a']==-1)
                nxt[now][buf[i]-'a']=newnode();
            now=nxt[now][buf[i]-'a'];
        }
        id[idd]=now;
    }
    void build(){
        stack<int>q;
        q.push(root);
        d[root]=0;
        int idx=0;
        while(q.size()){
            int now=q.top();q.pop();
            dfn[now]=++idx;
            for(int i=0;i<26;i++)
                if(nxt[now][i]!=-1){
                    q.push(nxt[now][i]);
                    d[nxt[now][i]]=d[now]+1;
                    f[nxt[now][i]][0]=now;
                }
        }
        f[1][0]=1;
        for(int i=1;i<=20;i++)
            for(int j=1;j<=L;j++)
                f[j][i]=f[f[j][i-1]][i-1];
    } 
}tr;

int cmp(int a,int b){
    return dfn[id[a]]<dfn[id[b]];
}
int LCA(int u,int v){
    if(d[u]<d[v])swap(u,v);
    int dif=d[u]-d[v];
    for(int i=20;i>=0;i--)
        if((dif>>i) & 1)u=f[u][i];
    if(u==v)return u;
    for(int i=20;i>=0;i--)
        if(f[u][i]!=f[v][i])
            u=f[u][i],v=f[v][i];
    return f[u][0]; 
}

vector<int>G[N];
inline void add(int u,int v){G[u].push_back(v);}
int size[N],stk[N],top,ans;//虚树 
void insert(int x){
    if(x==1)return;
    int t=LCA(x,stk[top]);
    if(t!=stk[top]){
        while(top>1 && dfn[stk[top-1]]>dfn[t]){
            add(stk[top-1],stk[top]);
            top--;
        }
        if(dfn[t]>dfn[stk[top-1]]){
            G[t].clear();
            add(t,stk[top]);
            stk[top]=t;
        }else {
            add(t,stk[top]);
            top--;
        }
    }
    G[x].clear();
    stk[++top]=x;
} 
void dfs(int u,int pre){//在虚树中dfs 
    //cout<<u<<" ";
    size[u]=vis[u];
    for(auto v:G[u])if(v!=pre){
        dfs(v,u);
        size[u]+=size[v];
    }
    if(size[u]==L)
        ans+=d[u]-d[pre];
}

char buf[N];
int a[N];
int main(){
    //freopen("3.in","r",stdin);
    //freopen("3.out","w",stdout);
    cin>>n>>Q;
    tr.init();
    for(int i=1;i<=n;i++){//插入第i个字符串 
        scanf("%s",buf);
        tr.insert(buf,i);
    }
    tr.build();

    
    while(Q--){
        scanf("%d%d",&K,&L);
        for(int i=1;i<=K;i++)scanf("%d",&a[i]);
        sort(a+1,a+1+K,cmp);
        for(int i=1;i<=K;i++)vis[id[a[i]]]=1;//标记终止结点 
        
        G[1].clear();
        top=0;stk[++top]=1;
        for(int i=1;i<=K;i++)
            insert(id[a[i]]);//建立虚树 
        for(int i=1;i<top;i++)
            add(stk[i],stk[i+1]);
        /*for(int i=1;i<=tr.L;i++){
            cout<<i<<" ";
            for(auto v:G[i])cout<<v<<" ";
            puts("");
        }*/
        ans=0;
        dfs(1,1);
        cout<<ans<<'
';
        
        for(int i=1;i<=K;i++)vis[id[a[i]]]=0;
    }
}
原文地址:https://www.cnblogs.com/zsben991126/p/12813281.html