构造完全图 Codgic1352 最小完全图 Codevs2796

【问题描述】 对于完全图G,若有且仅有一棵最小生成树为T,则称完全图G是树T的扩展出的。给你一 棵树T,找出T能扩展出的边权和最小的完全图G。

【文件输入】 第一行N表示树T的点数。 接下来N-1行:Si,Ti,Di;描述一条边(Si,Ti)权值为 Di。 保证输入数据构成一棵树。

【文件输出】 一个数,表示最小的图G的边权和。

【样例输入】 4 1 2 1 1 3 1 1 4 2

【样例输出】 12

【样例说明】 添加D(2,3)=2,D(3,4)=3,D(2,4)=3即可。

【数据范围】 对于20%的数据,N<=10 对于50%的数据,N<=1000 对于100%的数据,N<=100000,1<=Di<=100000

 

首先,作为一名OIer,看到最小生成树,脑海中一定会浮现Prim算法和Kruskal算法。

我们可以从Prim算法生成最小生成树的过程中获得灵感:

对于一个图的最小生成树中的每一条边 E<s,t>,都有:E在与s相连的所有边中权值最小,且E在与t相连的所有边中权值最小。

这个结论同样适用于本题的完全图G。

由于本题给出的是一个最小生成树T,所以我们可以知道,T中的每一条边都是一条割边。

去掉任意一条树T中的边,这个树一定会变成两个联通块,令其为A、B。要将T扩展为完全图G,那么显然 A中的每个点都需要与B中的所有点相连。

由之前推出的结论可得,A中新发出的连边的权值一定是大于(若等于则存在多种最小生成树解,不合题意)发出点在树上的相接边的权值的。

再一看数据量,单独枚举肯定不行。由联通块可以联想到并查集,发现可行。

对于一条最小生成树上的边E<A,B>,可以看做E连接了A,B两个联通块,那么要将A、B两块连接为一个完全图需要加的边数就是 cnt[A]*cnt[B]-1,其中cnt表示联通块中的结点个数。

由结论可得,这些边的权值一定是大于E的权值的,要使其最小,那么这些边的权值都是 E的权值+1

那么合并A、B两个联通块的花费就是 (边E.权+1)*(cnt[A]*cnt[B]-1)。ans在每次合并联通块时加上花费即可求解。

另外一点,因为要求的是最小的完全图,所以有一个贪心策略:先把树上的边按照权值从小到大排序,然后枚举即可。

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<cmath>
 5 #include<string>
 6 using namespace std;
 7 template<class T> inline void read(T &_a){
 8     bool f=0;int _ch=getchar();_a=0;
 9     while(_ch<'0' || _ch>'9'){if(_ch=='-')f=1;_ch=getchar();}
10     while(_ch>='0' && _ch<='9'){_a=(_a<<1)+(_a<<3)+_ch-'0';_ch=getchar();}
11     if(f)_a=-_a;
12 }
13 
14 const int maxn=100001;
15 struct edge
16 {
17     int from,to,next; long long dis;
18     bool operator < (const edge x) const {return dis<x.dis;}
19 }w[maxn];
20 int n,fa[maxn];
21 long long ans,cnt[maxn];
22 
23 int find(int u)
24 {
25     return fa[u]==u?u:fa[u]=find(fa[u]);
26 }
27 
28 int main()
29 {
30     freopen("tree.in","r",stdin);
31     freopen("tree.out","w",stdout);
32     read(n);
33     for (register int i=1;i<n;++i) read(w[i].from),read(w[i].to),read(w[i].dis),ans+=w[i].dis;
34     for (register int i=1;i<=n;++i) fa[i]=i,cnt[i]=1;
35     sort(w+1,w+n);
36     for (register int i=1;i<n;++i)
37     {
38         int a1=find(w[i].from);
39         int a2=find(w[i].to);
40         ans+=(w[i].dis+1)*(cnt[a1]*cnt[a2]-1);
41         fa[a1]=a2;
42         cnt[a2]+=cnt[a1];
43     }
44     printf("%lld",ans);
45     return 0;
46 }
View Code

 

posted @ 2017-10-19 14:00  JayWang  阅读(1334)  评论(0编辑  收藏  举报