P2495 [SDOI2011]消耗战
题目
一棵树,边有边权,每次给 \(m\) 个关键点,要求求出使得这 \(m\) 个点不能从根节点出发到达,需要断掉的边的边权总和的最小值。
分析
虚树模板题。
首先看到特征关键点,还有数据范围的求和符号,很明显是虚树。
于是我们可以发现题目要求的,我们可以先把虚树建出来,因为如果不在虚树上,那么边和点是没有用的。
这里要注意的是建立虚树的时候,边权就是当前路径上所有边权的最小值,这个可以倍增也可以树剖随便求一下。
这里使用的是更简单的办法,也就是直接预处理每个点到根节点的最小值,\(dp\) 方程很好写,暂不赘述。
代码
#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
x=0;bool f=false;char ch=getchar();
while(!isdigit(ch)){f|=ch=='-';ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=f?-x:x;
return ;
}
template <typename T>
inline void write(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10^48);
return ;
}
#define int long long
const int N=1e6+5,t=20,INF=1e18+7;
int n,m;
int head[N],to[N],nex[N],val[N],idx;
inline void add(int u,int v,int w){
nex[++idx]=head[u];
to[idx]=v;
val[idx]=w;
head[u]=idx;
return ;
}
int dfn[N],Min[N],dp[N],q[N],sta[N],k,top,DFN;
bool vis[N];
int Top[N],dep[N],siz[N],fa[N],son[N];
void dfs1(int x,int f,int fr){
siz[x]=1;dep[x]=dep[f]+1;fa[x]=f;Min[x]=min(Min[f],val[fr]);
for(int i=head[x];i;i=nex[i]){
int y=to[i];
if(y==f) continue;
dfs1(y,x,i);siz[x]+=siz[y];
if(siz[son[x]]<siz[y]) son[x]=y;
}
return ;
}
void dfs2(int x,int f){
if(son[fa[x]]==x) Top[x]=Top[f];
else Top[x]=x;
dfn[x]=++DFN;
if(son[x]) dfs2(son[x],x);
for(int i=head[x];i;i=nex[i]){
int y=to[i];
if(y==f||y==son[x]) continue;
dfs2(y,x);
}
return ;
}
int QueryLca(int x,int y){
while(Top[x]!=Top[y]){
if(dep[Top[x]]<dep[Top[y]]) swap(x,y);
x=fa[Top[x]];
}
return dep[x]<dep[y]?x:y;
}
inline bool Cmp(const int &x,const int &y){return dfn[x]<dfn[y];}
vector<int> vec[N];
void BuildVTree(){
sort(q+1,q+k+1,Cmp);
int cnt=0;sta[++cnt]=1;
for(int j=1;j<=k;j++){
if(q[j]==1) continue;
int x=q[j];
if(cnt==1){sta[++cnt]=x;continue;}
int lca=QueryLca(sta[cnt],x);
if(lca==sta[cnt]){sta[++cnt]=x;continue;}
while(cnt>1&&dfn[sta[cnt-1]]>=dfn[lca]) vec[sta[cnt-1]].push_back(sta[cnt]),cnt--;
if(lca!=sta[cnt]) vec[lca].push_back(sta[cnt]),sta[cnt]=lca;
sta[++cnt]=x;
}
while(cnt>1) vec[sta[cnt-1]].push_back(sta[cnt]),cnt--;
return ;
}
void DP(int x){
dp[x]=Min[x];
if(vis[x]) return ;int res=0;
for(auto v:vec[x]) DP(v),res+=dp[v];
dp[x]=min(dp[x],res);
return ;
}
void ReMove(int x){
dp[x]=INF;vis[x]=false;
for(auto v:vec[x]) ReMove(v);
vec[x].clear();
return ;
}
signed main(){
read(n);
for(int i=1;i<=n;i++) dp[i]=Min[i]=INF;
for(int i=1;i<n;i++){
int u,v,w;
read(u),read(v),read(w);
add(u,v,w),add(v,u,w);
}
read(m);val[0]=INF,Min[0]=INF;
dfs1(1,0,0);dfs2(1,0);
while(m--){
read(k);
for(int i=1;i<=k;i++) read(q[i]),vis[q[i]]=true;
BuildVTree();
DP(1);
write(dp[1]),putchar('\n');
ReMove(1);
}
return 0;
}