[模板] 虚树

用途

树上多次询问,每次询问关于一些关键点,关键点总个数给定限制;单拿出来每个询问,基本上树形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
",f[1]);
107         for(j=1;j<=K;j++)
108             flag[ky[j]]=0;
109     }
110     return 0;
111 }
View Code
原文地址:https://www.cnblogs.com/Ressed/p/9811784.html