【BZOJ3611】大工程(虚树,动态规划)

【BZOJ3611】大工程(虚树,动态规划)

题面

BZOJ

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

题解

先考虑正常的\(dp\)
对于第一个,总和。设\(f[i]\)表示\(i\)的子树中的关键点的个数
转移:\(sum+=f[v]*(K-f[v])*len(i,v),f[i]+=f[v]\)
对于第二个和第三个,相当于维护树上最长链和最短链
这个就非常基础了,不写了。

现在再把这个\(dp\)放在虚树上做就行了

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 1001000
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
struct Line{int v,next,w;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v,int w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
int n,Q,K;
int fa[MAX],size[MAX],hson[MAX],dep[MAX],top[MAX],dfn[MAX],low[MAX],tim;
void dfs1(int u,int ff)
{
	fa[u]=ff;dep[u]=dep[ff]+1;size[u]=1;
	for(int i=h[u];i;i=e[i].next)
	{
		int v=e[i].v;if(v==ff)continue;
		dfs1(v,u);size[u]+=size[v];
		if(size[v]>size[hson[u]])hson[u]=v;
	}
}
void dfs2(int u,int tp)
{
	top[u]=tp;dfn[u]=++tim;
	if(hson[u])dfs2(hson[u],tp);
	for(int i=h[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(v==hson[u]||v==fa[u])continue;
		dfs2(v,v);
	}
	low[u]=tim;
}
int LCA(int u,int v)
{
	while(top[u]^top[v])(dep[top[u]]<dep[top[v]])?v=fa[top[v]]:u=fa[top[u]];
	return dep[u]<dep[v]?u:v;
}
int S[MAX],p[MAX<<1];
bool vis[MAX];
bool cmp(int u,int v){return dfn[u]<dfn[v];}
int Min,Max,KK;
ll Sum;
int f1[MAX],f2[MAX],f3[MAX];
void DP(int u)
{
	if(vis[u])f1[u]=1,f2[u]=0,f3[u]=0;
	else f1[u]=0,f2[u]=1e9,f3[u]=-1e9;
	for(int i=h[u];i;i=e[i].next)
	{
		int v=e[i].v;DP(v);
		Sum+=1ll*f1[v]*(KK-f1[v])*e[i].w;f1[u]+=f1[v];
		Min=min(Min,f2[u]+f2[v]+e[i].w);f2[u]=min(f2[u],f2[v]+e[i].w);
		Max=max(Max,f3[u]+f3[v]+e[i].w);f3[u]=max(f3[u],f3[v]+e[i].w);
	}
}
int main()
{
	n=read();
	for(int i=1;i<n;++i)
	{
		int u=read(),v=read();
		Add(u,v,0);Add(v,u,0);
	}
	dfs1(1,0);dfs2(1,1);
	memset(h,0,sizeof(h));
	Q=read();
	while(Q--)
	{
		K=KK=read();cnt=1;int tp=0;
		for(int i=1;i<=K;++i)vis[p[i]=read()]=true;
		sort(&p[1],&p[K+1],cmp);
		for(int i=K;i>1;--i)p[++K]=LCA(p[i],p[i-1]);
		sort(&p[1],&p[K+1],cmp);K=unique(&p[1],&p[K+1])-p-1;
		for(int i=1;i<=K;++i)
		{
			while(tp&&low[S[tp]]<dfn[p[i]])--tp;
			Add(S[tp],p[i],dep[p[i]]-dep[S[tp]]);
			S[++tp]=p[i];
		}
		Sum=0;Min=1e9;Max=-1e9;DP(p[1]);
		printf("%lld %d %d\n",Sum,Min,Max);
		for(int i=1;i<=K;++i)vis[p[i]]=false,h[p[i]]=0;
	}
	return 0;
}

posted @ 2018-05-21 14:37  小蒟蒻yyb  阅读(284)  评论(0编辑  收藏  举报