BZOJ3611: [Heoi2014]大工程 虚树学习笔记

BZOJ3611: [Heoi2014]大工程

Description

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

Input

第一行 n 表示点数。

 接下来 n-1 行,每行两个数 a,b 表示 a 和 b 之间有一条边。
点从 1 开始标号。 接下来一行 q 表示计划数。
对每个计划有 2 行,第一行 k 表示这个计划选中了几个点。
 第二行用空格隔开的 k 个互不相同的数表示选了哪 k 个点。

Output

输出 q 行,每行三个数分别表示代价和,最小代价,最大代价。 

Sample Input

10
2 1
3 2
4 1
5 2
6 4
7 5
8 6
9 7
10 9
5
2
5 4
2
10 4
2
5 2
2
6 1
2
6 1

Sample Output

3 3 3
6 6 6
1 1 1
2 2 2
2 2 2

HINT

n<=1000000 

q<=50000并且保证所有k之和<=2*n 

题解Here!

看到$\sum k\leq 2\times 10^6$就已经确定是在虚树上跑$DP$了。。。
不知道虚树的可以看这个:

虚树学习笔记

然后看看$DP$怎么搞。

第一问显然是直接对每一条边计算它的贡献。

设$num[x]$表示$x$的子树内有多少个选中的点,一共有$m$个选中的点。

于是每一条边会被$num[x]\times(m-num[x])$条路径覆盖。

为什么?

因为我们相当于从$num[x],\text{即}x\text{的子树中}$选一个点,再在$m-num[x],\text{即}x\text{的子树外}$选一个点。

然后把所有的贡献加起来就是答案。

记得开$logn\ long$。

对于二、三两问,其实他俩的道理是类似的。

设$f[x]$表示在$x$的子树内距离$x$最近的选中的点到$x$的距离,$g[x]$表示在$x$的子树内距离$x$最远的选中的点到$x$的距离。

维护长这个样:

$$f[x]=\min\{\ f[v]+dis(x,v)\ |\ v\in son_x\ \}$$

$$g[x]=\max\{\ g[v]+dis(x,v)\ |\ v\in son_x\ \}$$

答案怎么更新呢?

其实很简单,如果在$x$的子树中已经遍历过的点中有选中的点,则更新答案:

$$Ans\_min=\min\{\ f[x]+dix(x,v)+f[v]\ |\ v\in son_x\ \}$$

$$Ans\_max=\max\{\ g[x]+dix(x,v)+g[v]\ |\ v\in son_x\ \}$$

于是这两个问题也解决了。

$LCA$的话,树剖就好。

剩下的不多说,可以看代码。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 1000010
#define MAX 999999999
using namespace std;
int n,m,q,c=1,d=1,e=1;
int head_a[MAXN],head_b[MAXN],deep[MAXN],son[MAXN],size[MAXN],fa[MAXN],id[MAXN],top[MAXN];
int top_stack,minn,maxn,maxi,h[MAXN],stack[MAXN],f[MAXN],g[MAXN],num[MAXN];
long long sum;
bool choose[MAXN];
struct Tree{
	int next,to;
}a[MAXN<<1];
struct New_Tree{
	int next,to,w;
}b[MAXN<<1];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
inline bool cmp(const int &p,const int &q){
	return id[p]<id[q];
}
inline void add_a(int x,int y){
	a[c].to=y;a[c].next=head_a[x];head_a[x]=c++;
	a[c].to=x;a[c].next=head_a[y];head_a[y]=c++;
}
inline void add_b(int u,int v,int w){
	b[e].to=v;b[e].w=w;b[e].next=head_b[u];head_b[u]=e++;
}
void dfs1(int rt){
	son[rt]=0;size[rt]=1;
	for(int i=head_a[rt];i;i=a[i].next){
		int will=a[i].to;
		if(!deep[will]){
			deep[will]=deep[rt]+1;
			fa[will]=rt;
			dfs1(will);
			size[rt]+=size[will];
			if(size[will]>size[son[rt]])son[rt]=will;
		}
	}
}
void dfs2(int rt,int f){
	id[rt]=d++;top[rt]=f;
	if(son[rt])dfs2(son[rt],f);
	for(int i=head_a[rt];i;i=a[i].next){
		int will=a[i].to;
		if(will!=fa[rt]&&will!=son[rt])dfs2(will,will);
	}
}
int LCA(int x,int y){
	while(top[x]!=top[y]){
		if(deep[top[x]]<deep[top[y]])swap(x,y);
		x=fa[top[x]];
	}
	if(deep[x]>deep[y])swap(x,y);
	return x;
}
void rebuild(){
	int x,dis,lca;
	top_stack=1;
	stack[top_stack]=1;
	sort(h+1,h+m+1,cmp);
	for(int i=1;i<=m;i++){
		choose[h[i]]=true;
		if(h[i]==1)continue;
		x=h[i];
		lca=LCA(x,stack[top_stack]);
		while(top_stack>1&&deep[stack[top_stack-1]]>deep[lca]){
			dis=deep[stack[top_stack]]-deep[stack[top_stack-1]];
			add_b(stack[top_stack-1],stack[top_stack],dis);
			stack[top_stack--]=0;
		}
		if(deep[lca]<deep[stack[top_stack]]){
			dis=deep[stack[top_stack]]-deep[lca];
			add_b(lca,stack[top_stack],dis);
			stack[top_stack--]=0;
		}
		if(deep[lca]>deep[stack[top_stack]])stack[++top_stack]=lca;
		stack[++top_stack]=x;
	}
	while(top_stack>1){
		dis=deep[stack[top_stack]]-deep[stack[top_stack-1]];
		add_b(stack[top_stack-1],stack[top_stack],dis);
		stack[top_stack--]=0;
	}
}
void solve(int rt){
	num[rt]=choose[rt];g[rt]=0;f[rt]=(choose[rt]?0:MAX);
	int will,w;
	for(int i=head_b[rt];i;i=b[i].next)solve(b[i].to);
	for(int i=head_b[rt];i;i=b[i].next){
		will=b[i].to;w=b[i].w;
		sum+=1LL*(m-num[will])*num[will]*w;
		if(num[rt]){
			minn=min(minn,f[rt]+w+f[will]);
			maxn=max(maxn,g[rt]+w+g[will]);
		}
		f[rt]=min(f[rt],f[will]+w);
		g[rt]=max(g[rt],g[will]+w);
		num[rt]+=num[will];
	}
	head_b[rt]=0;choose[rt]=false;
}
void work(){
	while(q--){
		e=1;sum=maxn=0;minn=MAX;
		m=read();
		for(int i=1;i<=m;i++)h[i]=read();
		rebuild();
		solve(1);
		printf("%lld %d %d\n",sum,minn,maxn);
	}
}
void init(){
	int x,y;
	n=read();
	for(int i=1;i<n;i++){
		x=read();y=read();
		add_a(x,y);
	}
	q=read();
	deep[1]=1;
	dfs1(1);
	dfs2(1,1);
}
int main(){
	init();
	work();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Yangrui-Blog/p/10630368.html