题解 DTOJ #1008.生成输入数据 (input)

欢迎访问 My Luogu Space


【题目描述】

首先看到题目别太开心,这题可不是让你出数据~^_*

背景神马的就忽略了。这题就是给你一棵带边权的树,然后这棵树是某个完全图唯一的最小生成树。问原来的完全图中所有边可能的最小边权和是多少。

完全图是任意两个点之间都有边相连的图。

【 输入输出格式】

输入格式:

第一行包含一个整数 \(T\) 表示数据组数。

每组数据第一行一个整数 \(N\) 表示点数。

接下来 \(N-1\) 行每行三个整数 \(a_i,b_i,w_i\) 表示最小生成树上 \(a_i\)\(b_i\) 之间有一条权值为 \(w_i\) 的边。

输出格式:

输出应有 \(T\) 行,每行表示一组数据的答案。

【输入输出样例】

输入样例:

2
3
1 2 4
2 3 7
4
1 2 1
1 3 1
1 4 2

输出样例:

19
12

【提示】

【数据范围】

\(20\%\) 的数据满足:\(T≤5,n≤5,w_i≤5\)
另外 \(30\%\) 的数据满足:\(n≤1000\),给定的树是一条链。
\(100\%\) 的数据满足:\(T≤10,n≤20000,w_i≤10000\)


【标签】

图论、最小生成树、kruskal


【分析】

kruskal。

基本想法

根据最小生成树的定义,可知:对于一个点我们自己添加的边的边权必须比其原本有的所有连边大。我们可以依次扫描每一个点,对每一个点向其他所有未连的点连接边权为已经和当前点相连的边的最大值+1,并同时统计答案。

改进

我们考虑到当我们在建最小生成树的时候,将边按边权从小到大排序,开始时每个点独自为一棵树,并且逐渐将所有树合并成一颗树。在处理每一条边的时候,已经添加的边一定比当前处理的边边权更小,此时我们可以直接从这条边所连接的两个联通块中的每一个点向另一个联通块中的每一个点连接一条边权为 \(w+1\) 的边。使得这两个联通块变成一个完全图。由于树是从小到大合并的,所以一定能保证每次操作的时候相当于两个完全图合并成一个完全图。


【代码】

[C++]

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
struct Edge{
	int a, b, w;
	bool operator<(const Edge &T)const{
		return w < T.w;
	}
}E[20005];

int T, Fa[20005], Siz[20005];

int Find(int k){return Fa[k]==k ? k : Fa[k]=Find(Fa[k]);}
int main(){
	scanf("%d", &T);
	while(T--){
		int n; LL ans = 0;
		memset(Siz, 0, sizeof Siz);
		scanf("%d", &n);
		for(int i=1; i<=n; ++i) Fa[i] = i, Siz[i] = 1;
		for(int i=1; i<n; ++i) scanf("%d%d%d", &E[i].a, &E[i].b, &E[i].w);
		sort(E+1, E+n);
		for(int i=1; i<n; ++i){
			int a = Find(E[i].a), b = Find(E[i].b);
			ans += 1ll*Siz[a]*Siz[b]*(E[i].w+1)-1;
			Siz[a] += Siz[b], Fa[b] = a;
		}
		printf("%lld\n", ans);
	}
	return 0;
}
posted @ 2019-03-21 12:06  Nelson_Wang  阅读(195)  评论(0编辑  收藏  举报