[模板] 虚树
用途
树上多次询问,每次询问关于一些关键点,关键点总个数给定限制;单拿出来每个询问,基本上树形dp可以解决
思路
把每次询问的点和询问的点的lca(即关键点)浓缩到虚树上,两点之间的连边包含原树中两点间路径的信息,再在虚树上暴力(?)处理
做法
先yy一个在原树上对于单一询问的做法,然后把它放到虚树上来做
那么怎么建虚树呢
先按照dfs序排序,这样可以保证做到不在x的子树中的点时,x的子树中的点都已经做完了
用一个栈来记录从虚树根到当前做到的点的链。虽然树根是谁都行,但方便起见直接给成1,并把1和第一个点压入栈中
现在新加入一个点p,设栈顶元素是x,p和x的最近公共祖先是lca
有两种情况(lca绝对不会等于p,因为是按dfs序做的):
1.lca=x,直接把p压入栈中
2.lca!=x
这说明lca在x的上面,而且x的子树已经做完了,我们要在栈中找到一个合适的位置把lca放下来,再把x连到一个合适的点上,再把x踢掉,再把p加进去
设栈顶元素是y,再记一个last表示刚才踢掉的元素
当dfn[y]>dfn[lca]时,循环地,把last连到y上(如果有的话),然后把y也踢掉
当跳出循环时,有可能lca=y,如果不等的话,就把lca压到栈中作为栈顶
再把刚才还没连的那个last连到lca上,最后把p也压到栈里。
注意连边的时候记录路径信息
这样在建出的虚树点数是$O(k)$的(k是询问点数),在他上面上乱搞就可以了。
注意由于询问数很多,万万不可memset,为了方便清空,可以记下来树中的每个点,然后依次清零
例题
luogu2495 消耗战
建出虚树后是一个比较显然的treedp(写的很丑所以只看build好了)
1 #include<bits/stdc++.h> 2 #define pa pair<int,int> 3 #define CLR(a,x) memset(a,x,sizeof(a)) 4 using namespace std; 5 typedef long long ll; 6 const int maxn=25e4+10,inf=1e9; 7 8 inline ll rd(){ 9 ll x=0;char c=getchar();int neg=1; 10 while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();} 11 while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); 12 return x*neg; 13 } 14 15 int eg[maxn*2][3],egh[maxn],ect; 16 int N,M,K; 17 int dep[maxn],fa[maxn][20],dfn[maxn],md[maxn][20],tot; 18 int ky[maxn],stk[maxn],sh; 19 int son[maxn],bro[maxn][2]; 20 ll f[maxn]; 21 bool flag[maxn]; 22 23 inline void adeg(int a,int b,int c){ 24 eg[++ect][0]=b,eg[ect][2]=c;eg[ect][1]=egh[a];egh[a]=ect; 25 } 26 27 void dfs(int x){ 28 for(int i=0;fa[x][i]&&fa[fa[x][i]][i];i++){ 29 fa[x][i+1]=fa[fa[x][i]][i]; 30 md[x][i+1]=min(md[x][i],md[fa[x][i]][i]); 31 } 32 dfn[x]=++tot; 33 for(int i=egh[x];i;i=eg[i][1]){ 34 int b=eg[i][0]; 35 if(b==fa[x][0]) continue; 36 md[b][0]=eg[i][2]; 37 fa[b][0]=x,dep[b]=dep[x]+1; 38 dfs(b); 39 } 40 } 41 42 pa getlca(int x,int y){ 43 int mi=inf; 44 if(dep[x]<dep[y]) swap(x,y); 45 for(int i=log2(dep[x]-dep[y]);i>=0&&dep[x]!=dep[y];i--){ 46 if(dep[fa[x][i]]>=dep[y]) 47 mi=min(mi,md[x][i]),x=fa[x][i]; 48 } 49 if(x==y) return make_pair(x,mi); 50 for(int i=log2(dep[x]);i>=0;i--){ 51 if(fa[x][i]!=fa[y][i]) 52 mi=min(mi,min(md[x][i],md[y][i])),x=fa[x][i],y=fa[y][i]; 53 } 54 return make_pair(fa[x][0],min(mi,min(md[x][0],md[y][0]))); 55 } 56 57 inline bool cmp(int x,int y){return dfn[x]<dfn[y];} 58 59 inline void adson(int x,int y){ 60 bro[y][0]=son[x],son[x]=y,bro[y][1]=getlca(x,y).second; 61 } 62 63 inline void build(){ 64 stk[sh=1]=1; 65 for(int i=1;i<=K;i++){ 66 int lca=getlca(ky[i],stk[sh]).first; 67 int lst=0; 68 while(dfn[lca]<dfn[stk[sh]]){ 69 if(lst) adson(stk[sh],lst); 70 lst=stk[sh--]; 71 } 72 if(lca!=stk[sh]) stk[++sh]=lca; 73 if(lst) adson(stk[sh],lst); 74 if(ky[i]!=1) stk[++sh]=ky[i]; 75 } 76 while(sh>1) adson(stk[sh-1],stk[sh]),sh--; 77 } 78 79 void dp(int x){ 80 f[x]=0; 81 for(int i=son[x];i;i=bro[i][0]){ 82 dp(i); 83 if(!flag[i]) f[x]+=min(f[i],1ll*bro[i][1]); 84 else f[x]+=bro[i][1]; 85 } 86 son[x]=0; 87 } 88 89 int main(){ 90 //freopen("","r",stdin); 91 int i,j,k; 92 N=rd(); 93 for(i=1;i<N;i++){ 94 int a=rd(),b=rd(),c=rd(); 95 adeg(a,b,c);adeg(b,a,c); 96 } 97 dep[1]=1;dfs(1); 98 M=rd(); 99 for(i=1;i<=M;i++){ 100 K=rd(); 101 for(j=1;j<=K;j++) 102 ky[j]=rd(),flag[ky[j]]=1; 103 sort(ky+1,ky+K+1,cmp); 104 build(); 105 dp(1); 106 printf("%lld\n",f[1]); 107 for(j=1;j<=K;j++) 108 flag[ky[j]]=0; 109 } 110 return 0; 111 }