3611: [Heoi2014]大工程

5 篇文章 0 订阅

3611: [Heoi2014]大工程

Time Limit: 60 Sec   Memory Limit: 512 MB
Submit: 764   Solved: 344
[ Submit][ Status][ Discuss]

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 

Source

[ Submit][ Status][ Discuss]

首先构建虚树。。
然后在树上dp
MAX[x]代表x子树最长链MIN[x]最短
然后总路径的计算显然

苟蒻一开始老想着各数组维护连着他们向上的那条路一起。。但是这样写老是错
sad

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;

const int maxn = 1E6 + 10;
typedef long long LL;
const int INF = maxn*2;

struct E{
	int to,w;
};

int n,m,cur,d_c,road,fa[maxn][21],b[maxn],dfn[maxn],L[maxn],a[maxn],s[maxn];
LL a1,tot[maxn]; int a2,a3,siz[maxn],MIN[maxn],MAX[maxn];
bool p[maxn];

vector <E> v[maxn];
vector <int> v2[maxn];

void dfs(int x,int from)
{
	dfn[x] = ++d_c;
	for (int i = 1; i <= 20; i++) fa[x][i] = fa[fa[x][i-1]][i-1];
	for (int i = 0; i < v2[x].size(); i++) {
		int to = v2[x][i];
		if (to == from) continue;
		L[to] = L[x] + 1; fa[to][0] = x;
		dfs(to,x);
	}
}

bool cmp(const int &x,const int &y)
{
	return dfn[x] < dfn[y];
}

void dfs2(int x)
{
	/*if (!v[x].size()) {MIN[x] = MAX[x] = tot[x] = va; siz[x] = 1; return;}
	MIN[x] = p[x]?0:INF; MAX[x] = 0; siz[x] = 0; tot[x] = 0;
	for (int i = 0; i < v[x].size(); i++) {
		int to = v[x][i].to;
		int w = v[x][i].w;
		dfs2(to,w);
		a1 += 1LL*siz[x]*tot[to] + 1LL*siz[to]*tot[x];
		if (p[x]) {
			a1 += tot[to]; 
			a2 = min(a2,MIN[to]); a3 = max(a3,MAX[to]+MAX[x]);
		}
		else {
			if (MIN[to] != INF && MIN[x] != INF) a2 = min(a2,MIN[to]+MIN[x]);
			if (MAX[x]) a3 = max(a3,MAX[to]+MAX[x]);	
		}
		siz[x] += siz[to]; tot[x] += tot[to];
		MIN[x] = min(MIN[x],MIN[to]);
		MAX[x] = max(MAX[x],MAX[to]);
	}
	MAX[x] += va; MIN[x] += va;
	if (p[x]) ++siz[x],MIN[x] = va; tot[x] += 1LL*siz[x]*va;*/
	MIN[x] = p[x]?0:INF; MAX[x] = p[x]?0:-INF;
	siz[x] = p[x]?1:0; tot[x] = 0;
	for (int i = 0; i < v[x].size(); i++) {
		int to = v[x][i].to;
		int w = v[x][i].w;
		dfs2(to);
		a1 += (tot[x] + 1LL*siz[x]*w)*1LL*siz[to] + 1LL*siz[x]*tot[to];
		a2 = min(a2,MIN[x] + MIN[to] + w);
		a3 = max(a3,MAX[x] + MAX[to] + w);
		MIN[x] = min(MIN[x],MIN[to] + w);
		MAX[x] = max(MAX[x],MAX[to] + w);
		siz[x] += siz[to]; tot[x] += tot[to] + 1LL*siz[to]*w;
	}
}

int LCA(int x,int y)
{
	if (L[x] < L[y]) swap(x,y); road = 0;
	int Log; for (Log = 0; L[x] - (1<<Log) >= 1; Log++); --Log;
	for (int j = Log; j >= 0; j--)
		if (L[x] - (1<<j) >= L[y])
			road += (1<<j),x = fa[x][j];
	if (x == y) return x;
	for (int j = Log; j >= 0; j--)
		if (fa[x][j] != fa[y][j])
			x = fa[x][j],y = fa[y][j];
	return fa[x][0];
}

int main()
{
	#ifdef YZY
		   freopen("tree2.in","r",stdin);
	#endif
	
	cin >> n;
	for (int i = 1; i < n; i++) {
		int x,y; scanf("%d%d",&x,&y);
		v2[x].push_back(y); v2[y].push_back(x);
	}
	L[1] = 1; dfs(1,0);
	
	cin >> m;
	for (cur = 1; cur <= m; cur++) {
		int k; scanf("%d",&k);
		for (int i = 0; i < k; i++) scanf("%d",&a[i]),p[a[i]] = 1;
		sort(a,a+k,cmp); int root = a[0]; 
		for (int i = 1; i < k; i++) root = LCA(root,a[i]);
		int cnt; s[cnt = 1] = root; int st = root == a[0]?1:0;
		for (int i = st; i < k; i++) {
			int Lca = LCA(a[i],s[cnt]);
			if (Lca != s[cnt]) 
				while (cnt > 1) {
					int lca = LCA(s[cnt],s[cnt-1]);
					if (dfn[lca] <= dfn[Lca]) {
						if (b[Lca] != cur) b[Lca] = cur,v[Lca].clear();
						LCA(s[cnt],Lca);
						v[Lca].push_back((E){s[cnt],road});
						if (dfn[lca] < dfn[Lca]) s[cnt] = Lca; else --cnt; 
						break;
					}
					if (b[s[cnt-1]] != cur) b[s[cnt-1]] = cur,v[s[cnt-1]].clear();
					v[s[cnt-1]].push_back((E){s[cnt],road}); --cnt;
 				}
			s[++cnt] = a[i];
		}
		while (cnt > 1) {
			LCA(s[cnt],s[cnt-1]);
			if (b[s[cnt-1]] != cur) b[s[cnt-1]] = cur,v[s[cnt-1]].clear();
			v[s[cnt-1]].push_back((E){s[cnt],road}); --cnt;
		}
		a2 = INF; a3 = 0; a1 = 0; dfs2(root); 
		printf("%lld %d %d\n",a1,a2,a3);
		for (int i = 0; i < k; i++) p[a[i]] = 0;
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值