【经典】虚树+字典树——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<<'\n'; for(int i=1;i<=K;i++)vis[id[a[i]]]=0; } }