【BZOJ2286】【SDOI2011】—消耗战(虚树)

传送门

虚树练手题


有一个显然的O(mn)O(mn)dpdp:

我们考虑对于每一个点处理出两个东西

f[i]:f[i]:表示点ii到根节点中最小的边

g[i]:g[i]:表示将ii的子树中关键点全部切断的最小花费,如果子树没有关键点则为0

显然每个点的答案就是min(f[i],g[i])min(f[i],g[i])

一次dpO(n)dpO(n)


接下来考虑如何优化

我们可以发现每次边权是不会变的

也就是说ff是不会变的

变的只是必须要切断的点

考虑到Σki<=500000\Sigma k_i<=500000

我们把每次会对答案造成影响的点单独拿出来dpdp

那么现在的问题就是怎么样找到会对答案造成影响的点

做法摘自自为风月马前卒

考虑得到了询问点,如何构造出一棵虚树。

首先我们要先对整棵树dfsdfs一遍,求出他们的dfsdfs序,然后对每个节点以dfsdfs序为关键字从小到大排序

同时维护一个栈,表示从根到栈顶元素这条链

假设当前要加入的节点为pp,栈顶元素为x=s[top]x=s[top]lcalca为他们的最近公共祖先
因为我们是按照dfsdfs序遍历,因此lcalca不可能是pp

那么现在会有两种情况:

lcalcaxx,直接将pp入栈。

x,px,p分别位于lcalca的两棵子树中,此时xx这棵子树已经遍历完毕,(如果没有,即xx的子树中还有一个未加入的点yy,但是dfn[y]<dfn[p]dfn[y]<dfn[p],即应先访问yy), 我们需要对其进行构建
设栈顶元素为xx,第二个元素为yy

dfn[y]>dfn[lca]dfn[y]>dfn[lca]可以连边y>xy>x,将xx出栈;

dfn[y]=dfn[lca]dfn[y]=dfn[lca]y=lcay=lca连边lca>xlca>x,此时子树构建完毕(break)(break)

dfn[y]<dfn[lca]dfn[y]<dfn[lca]lcalcay,xy,x之间,连边lca>xlca>xxx出栈,再将lcalca入栈。此时子树构建完毕(break)(break)

#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';
	}
}
posted @ 2019-01-25 17:42  Stargazer_cykoi  阅读(129)  评论(0编辑  收藏  举报