bzoj 3611: [Heoi2014]大工程 虚树

题目:

国家有一个大工程,要给一个非常大的交通网络里建一些新的通道。
我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上。
在 2 个国家 a,b 之间建一条新通道需要的代价为树上 a,b 的最短路径。
现在国家有很多个计划,每个计划都是这样,我们选中了 k 个点,然后在它们两两之间 新建 C(k,2)条 新通道。
现在对于每个计划,我们想知道:
1.这些新通道的代价和
2.这些新通道中代价最小的是多少
3.这些新通道中代价最大的是多少

题解:

这道题貌似是虚树的板子题.
为了这道题去学了学虚树.

因为我们发现我们只用在意两端都在关键点上的路径.
所以说实际上非关键点除了增加了一些路径的长度就没有其他的用处了.

我们可以考虑在原本的树上构造出一个只包括关键点但是赋予一定边一定的边权的与原树等价的树.
这就叫做虚树.
但是我们不能做到只保留关键点,我们仍然需要保留所有关键点之间的lca和lca之间的lca

对于虚树的构造方法有(O(nlongn))的,可以去看其他的讲解,这里不再叙述.

随后我们在虚树上进行dp,考虑每一条边对答案的贡献即可.

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
	x=0;char ch;bool flag = false;
	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const int maxn = 2100010;
const int inf = 0x3f3f3f3f;
ll f[maxn];int g[maxn][2];
int ans0,ans1,num;
bool col[maxn];
int son[maxn],top[maxn],dep[maxn];
int fa[maxn],dfn[maxn],dfs_clock;
struct Graph{
	struct Edge{
		int to,next;
	}G[maxn<<1];
	int head[maxn],cnt;
	void add(int u,int v){
		G[++cnt].to = v;
		G[cnt].next = head[u];
		head[u] = cnt;
	}
	int siz[maxn];
#define v G[i].to
	void dfs(int u){
		siz[u] = 1;
		for(int i = head[u];i;i=G[i].next){
			if(v == fa[u]) continue;
			fa[v] = u;
			dep[v] = dep[u] + 1;
			dfs(v);
			siz[u] += siz[v];
			if(siz[son[u]] < siz[v]) son[u] = v;
		}
	}
	void dfs(int u,int tp){
		top[u] = tp;dfn[u] = ++ dfs_clock;
		if(son[u]) dfs(son[u],tp);
		for(int i = head[u];i;i=G[i].next){
			if(v == fa[u] || v == son[u]) continue;
			dfs(v,v);
		}
	}
	void dp(int u,int fa){
		if(col[u]) siz[u] = 1;
		else siz[u] = 0;
		f[u] = 0;g[u][0] = inf;g[u][1] = 0;
		for(int i = head[u];i;i=G[i].next){
			if(v == fa) continue;
			dp(v,u);siz[u] += siz[v];
			int d = dep[v] - dep[u];
			f[u] += f[v] + 1LL*d*siz[v]*(num - siz[v]);
			ans0 = min(ans0,g[u][0] + d + g[v][0]);
			ans1 = max(ans1,g[u][1] + d + g[v][1]);
			g[u][0] = min(g[u][0],d + g[v][0]);
			g[u][1] = max(g[u][1],d + g[v][1]);
		}
		if(col[u]){
			ans0 = min(ans0,g[u][0]);
			ans1 = max(ans1,g[u][1]);
			g[u][0] = 0;
		}
		head[u] = 0;
		return ;
	}
#undef v

}pic1,pic2;
inline int lca(int u,int v){
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]]) swap(u,v);
		u = fa[top[u]];
	}return dep[u] < dep[v] ? u : v;
}
int sta[maxn],a[maxn];
inline bool cmp(const int &i,const int &j){
	return dfn[i] < dfn[j];
}
int main(){
	int n;read(n);
	for(int i=1,u,v;i<n;++i){
		read(u);read(v);
		pic1.add(u,v);
		pic1.add(v,u);
	}
	pic1.dfs(1);pic1.dfs(1,1);
	int q;read(q);
	while(q--){
		pic2.cnt = 0;
		read(num);
		for(int i=1;i<=num;++i){
			read(a[i]);
			col[a[i]] = true;
		}
		sort(a+1,a+num+1,cmp);
		int top = 0;
		for(int i=1;i<=num;++i){
			if(top == 0) sta[++top] = a[i];
			else{
				int lc = lca(sta[top],a[i]);
				while(dfn[sta[top]] > dfn[lc]){
					if(dfn[sta[top-1]] <= dfn[lc]){
						pic2.add(lc,sta[top]);
						if(sta[--top] != lc)sta[++top] = lc;
						break;
					}
					pic2.add(sta[top-1],sta[top]);
					-- top;
				}
				sta[++top] = a[i];
			}
		}
		while(top > 1) pic2.add(sta[top-1],sta[top]),top--;
		ans0 = inf;ans1 = 0;
		pic2.dp(sta[1],sta[1]);
		printf("%lld %d %d
",f[sta[1]],ans0,ans1);
		for(int i=1;i<=num;++i) col[a[i]] = false;
	}
	return 0;
}
原文地址:https://www.cnblogs.com/Skyminer/p/6592613.html