【BZOJ2286】【SDOI2011】—消耗战(虚树)
虚树练手题
有一个显然的的
我们考虑对于每一个点处理出两个东西
表示点到根节点中最小的边
表示将的子树中关键点全部切断的最小花费,如果子树没有关键点则为0
显然每个点的答案就是
一次
接下来考虑如何优化
我们可以发现每次边权是不会变的
也就是说是不会变的
变的只是必须要切断的点
考虑到
我们把每次会对答案造成影响的点单独拿出来
那么现在的问题就是怎么样找到会对答案造成影响的点
做法摘自自为风月马前卒
考虑得到了询问点,如何构造出一棵虚树。
首先我们要先对整棵树一遍,求出他们的序,然后对每个节点以序为关键字从小到大排序
同时维护一个栈,表示从根到栈顶元素这条链
假设当前要加入的节点为,栈顶元素为,为他们的最近公共祖先
因为我们是按照序遍历,因此不可能是
那么现在会有两种情况:
是,直接将入栈。
分别位于的两棵子树中,此时这棵子树已经遍历完毕,(如果没有,即的子树中还有一个未加入的点,但是,即应先访问), 我们需要对其进行构建
设栈顶元素为,第二个元素为
若可以连边,将出栈;
若即连边,此时子树构建完毕;
若即在之间,连边,出栈,再将入栈。此时子树构建完毕。
#include<bits/stdc++.h>
using namespace std;
inline int read(){
char ch=getchar();
int res=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
return res*f;
}
#define int long long
const int N=250005;
int n,m,adj[N],nxt[N<<1],cnt,to[N<<1];
int f[N],a[N],val[N<<1];
int fa[N],dep[N],siz[N],son[N],topf[N],tot,dfn[N],stk[N],top;
vector<int> e[N];
inline void addedge(int u,int v,int w){
nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v,val[cnt]=w;
}
inline void add(int u,int v){
e[u].push_back(v);
}
void dfs1(int u){
siz[u]=1;
for(int e=adj[u];e;e=nxt[e]){
int v=to[e];
if(v==fa[u])continue;
dep[v]=dep[u]+1,fa[v]=u;
f[v]=min(f[u],val[e]);
dfs1(v),siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
}
void dfs2(int u,int tp){
topf[u]=tp,dfn[u]=++tot;
if(!son[u])return;
dfs2(son[u],tp);
for(int e=adj[u];e;e=nxt[e]){
int v=to[e];
if(v==son[u]|v==fa[u])continue;
dfs2(v,v);
}
}
inline int Lca(int u,int v){
while(topf[u]!=topf[v]){
if(dep[topf[u]]<dep[topf[v]])v=fa[topf[v]];
else u=fa[topf[u]];
}
if(dep[u]<dep[v])swap(u,v);
return v;
}
inline void insert(int u){
if(top==1){stk[++top]=u;return;}
int lca=Lca(u,stk[top]);
if(lca==stk[top])return;
while(top>1&&dfn[stk[top-1]]>=dfn[lca])add(stk[top-1],stk[top]),--top;
if(lca!=stk[top])add(lca,stk[top]),stk[top]=lca;
stk[++top]=u;
}
inline bool comp(int a,int b){
return dfn[a]<dfn[b];
}
inline int dp(int u){
if(e[u].size()==0)return f[u];
int res=0;
for(int i=0;i<e[u].size();i++)
res+=dp(e[u][i]);
e[u].clear();
return min(res,f[u]);
}
signed main(){
f[1]=1ll<<50;
n=read();
for(int i=1;i<n;i++){
int u=read(),v=read(),w=read();
addedge(u,v,w),addedge(v,u,w);
}
dfs1(1),dfs2(1,1);
m=read();
for(int i=1;i<=m;i++){
int p=read();
for(int i=1;i<=p;i++)a[i]=read();
sort(a+1,a+p+1,comp);
stk[top=1]=1;
for(int i=1;i<=p;i++)insert(a[i]);
while(top > 0) add(stk[top - 1], stk[top]), top--;
cout<<dp(1)<<'\n';
}
}