P2495 【模板】虚树 / [SDOI2011] 消耗战
题意
给一颗带权树,每次询问给一些节点,需要断掉一些边,断掉这条边的代价为祂的边权,使得给出的节点都不与 \(1\) 联通,问最小代价。
思路
虚树板子题。
对于一次询问 \((m,h_1,h_2,\dots,h_m)\),把 \(h_1,h_2,\dots,h_m\) 按照 \(dfn\) 排序,然后相邻两个求 \(lca\),把 \(1,h_1,h_2,\dots,h_m\) 和求出来的所有 \(lca\) 都当作关键点,建出虚树,跑 \(dp\)。
考虑如何建虚树。把所有关键点按照 \(dfn\) 排序,每个点向祂和祂前面一个点的 \(lca\) 连边,边权是原树路径上边权最小值,显然,这样是不会跨过关键点连边的,因为如果跨过了关键点连边,那么被跨过的那个点的 \(dfn\) 是夹在当前点和前一个点的 \(dfn\) 之间的,而 \(dfn\) 是按照升序排序的,矛盾。
那么我们建出了虚树,设 \(f_{i,0/1}\) 表示 \(i\) 的子树内有/没有被切断的最小代价,转移 \(f_{u,1}=\sum_{v\in son(u)}\min(f_{v,1},f_{v,0}+val_{u,v})\ \ f_{u,0}=\sum_{v\in son(u)}\min(f_{v,1},f_{v,0})\),答案即为 \(f_{1,1}\)。
代码
/*
Luogu P2495 【模板】虚树 / [SDOI2011] 消耗战
2026-04-09
*/
#include<bits/stdc++.h>
using namespace std;
namespace IO{
template<typename T>
inline void read(T&x){
x=0;char c=getchar();bool f=0;
while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
f?x=-x:0;
}
template<typename T>
inline void write(T x){
if(x==0){putchar('0');return ;}
x<0?x=-x,putchar('-'):0;short st[50],top=0;
while(x) st[++top]=x%10,x/=10;
while(top) putchar(st[top--]+'0');
}
inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
inline void write(char c){putchar(c);}
inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
#define LL long long
const int maxn=250010,inf=1000000000;
const LL infLL=10000000000000000;
int n,dfn[maxn],cnt_dfn,fa[maxn][20],val[maxn][20],deep[maxn],m,h[maxn*2],cnt_h;
LL f[maxn][2];
bool flag[maxn];
vector<pair<int,int>>e[maxn],r[maxn];
void dfs(int u){
deep[u]=deep[fa[u][0]]+1;
for(int i=1;i<=18;i++) fa[u][i]=fa[fa[u][i-1]][i-1],val[u][i]=min(val[u][i-1],val[fa[u][i-1]][i-1]);
dfn[u]=++cnt_dfn;
for(auto[v,z]:e[u]){
if(v==fa[u][0]) continue;
fa[v][0]=u,val[v][0]=z;
dfs(v);
}
}
int lca(int u,int v){
if(deep[u]<deep[v]) swap(u,v);
for(int i=18;i>=0;i--) if(deep[fa[u][i]]>=deep[v]) u=fa[u][i];
if(u==v) return u;
for(int i=18;i>=0;i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
int query(int u,int v){
int ans=inf;
for(int i=18;i>=0;i--) if(deep[fa[u][i]]>=deep[v]) ans=min(ans,val[u][i]),u=fa[u][i];
return ans;
}
bool cmp(int a,int b){return dfn[a]<dfn[b];}
void dp(int u){
f[u][1]=f[u][0]=0;
if(flag[u]) f[u][1]=infLL;
for(auto[v,z]:r[u]){
dp(v);
f[u][1]+=min(f[v][1],f[v][0]+z);
f[u][0]+=min(f[v][1],f[v][0]);
}
}
signed main(){
read(n);
for(int i=1;i<n;i++){
int u,v,w;read(u,v,w);
e[u].push_back({v,w}),e[v].push_back({u,w});
}
dfs(1);
read(m);
for(int i=1;i<=m;i++){
int k;read(k);
for(int i=1;i<=k;i++) read(h[i]),flag[h[i]]=1;
cnt_h=k;
h[++cnt_h]=1;
sort(h+1,h+1+cnt_h,cmp);
for(int i=2;i<=k+1;i++) h[++cnt_h]=lca(h[i],h[i-1]);
sort(h+1,h+1+cnt_h);cnt_h=unique(h+1,h+1+cnt_h)-h-1;
sort(h+1,h+1+cnt_h,cmp);
for(int i=2;i<=cnt_h;i++){
int fa=lca(h[i],h[i-1]),val=query(h[i],fa);
if(fa==h[i]) continue;
r[fa].push_back({h[i],val});
}
dp(1);
for(int i=1;i<=cnt_h;i++) r[h[i]].clear(),flag[h[i]]=0;
write(f[1][1]);write("\n");
}
return 0;
}

浙公网安备 33010602011771号