BZOJ3906 : Trie
将输入的Trie建成AC自动机,并建出fail树。
那么操作1等价于在给定点的子树的并集里都加1。
操作2等价于查询给定点到根节点路径的并集的权值和。
求出DFS序后,对于操作1,将点按进入时间戳从小到大排序,然后求出并集,进行区间修改即可。
对于操作2,构造给定点集的虚树,在虚树的每一条边上询问权值和,累加起来即可。
对于子树修改,链查询,可以使用4棵树状数组维护。
时间复杂度$O((m+k)\log n)$。
#include<cstdio> #include<algorithm> #define N 100010 using namespace std; typedef long long ll; int Case,n,Q,i,j,x,op,ch[N][26],f[N],g[N],nxt[N]; int d[N],size[N],son[N],top[N],st[N],en[N],dfn; int m,q[N],a[N],tot,t,vis[N]; struct BIT{ int n,s[N<<1],a[N<<1];ll b[N<<1]; inline void init(int x){n=x;for(int i=1;i<=n;i++)a[i]=b[i]=s[i]=0;} inline void modify(int x,int p){for(int i=x;i<=n;i+=i&-i)a[i]+=p,b[i]+=p*s[x-1];} inline ll ask(int x){ int t0=0;ll t1=0; for(int i=x;i;i-=i&-i)t0+=a[i],t1+=b[i]; return 1LL*s[x]*t0-t1; } inline void add(int x,int y){modify(x,1),modify(y+1,-1);} }ti,to; inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';} inline int getch(){char c;while(!(((c=getchar())>='a')&&(c<='z')));return c;} inline void make(){ int h=1,t=0,i,j,x; for(i=0;i<26;i++)if(ch[1][i])f[q[++t]=ch[1][i]]=1;else ch[1][i]=1; while(h<=t)for(x=q[h++],i=0;i<26;i++)if(ch[x][i])f[ch[x][i]]=ch[f[x]][i],q[++t]=ch[x][i];else ch[x][i]=ch[f[x]][i]; } void dfs(int x){ d[x]=d[f[x]]+1,size[x]=1,son[x]=0; for(int i=g[x];i;i=nxt[i]){ dfs(i),size[x]+=size[i]; if(size[i]>size[son[x]])son[x]=i; } } void dfs2(int x,int y){ top[x]=y;st[x]=++dfn; if(son[x])dfs2(son[x],y); for(int i=g[x];i;i=nxt[i])if(i!=son[x])dfs2(i,i); en[x]=++dfn; } inline int lca(int x,int y){ for(;top[x]!=top[y];x=f[top[x]])if(d[top[x]]<d[top[y]])swap(x,y); return d[x]<d[y]?x:y; } inline int cmp(int x,int y){return st[x]<st[y];} int main(){ read(Case); while(Case--){ read(n); for(i=2;i<=n;i++)read(x),ch[x][getch()-'a']=i; make(); for(i=2;i<=n;i++)nxt[i]=g[f[i]],g[f[i]]=i; dfs(1),dfs2(1,1); ti.init(dfn),to.init(dfn); for(i=1;i<=n;i++)ti.s[st[i]]=1,to.s[en[i]]=-1; for(i=2;i<=dfn;i++)ti.s[i]+=ti.s[i-1],to.s[i]+=to.s[i-1]; read(Q); while(Q--){ read(op),read(m); if(op==1){ for(i=0;i<m;i++)read(a[i]); for(sort(a,a+m,cmp),j=i=0;i<m;i++)if(st[a[i]]>j){ ti.add(st[a[i]],en[a[i]]); to.add(st[a[i]],en[a[i]]); j=en[a[i]]; } }else{ for(tot=i=0;i<m;i++){ read(x); if(!vis[x])vis[a[++tot]=x]=1; } if(!vis[1])vis[a[++tot]=1]=1; m=tot,sort(a+1,a+m+1,cmp); for(i=1;i<m;i++)if(!vis[x=lca(a[i],a[i+1])])vis[a[++tot]=x]=1; m=tot,sort(a+1,a+m+1,cmp); ll ans=ti.ask(1); for(q[t=1]=a[1],i=2;i<=m;q[++t]=a[i++]){ while(st[a[i]]<st[q[t]]||en[a[i]]>en[q[t]])t--; ans+=ti.ask(st[a[i]])+to.ask(st[a[i]])-ti.ask(st[q[t]])-to.ask(st[q[t]]); } for(i=1;i<=m;i++)vis[a[i]]=0; printf("%lld\n",ans); } } for(dfn=0,i=1;i<=n;i++)for(f[i]=g[i]=j=0;j<26;j++)ch[i][j]=0; } return 0; }