• 走廊泼水节


    AcWing

    题意:给定一棵n个节点的树,要求增加若干条边,把这棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树.求增加的边的权值总和最小是多少?(n<=6000.)

    分析:今天才知道完全图是什么???完全图是一个简单的无向图,其中每对不同的顶点之间都恰有一条边相连---<<百度百科>>.

    那么我们还是按照(Kruskal)构建最小生成树的思想,把n条边按照权值从小到大排序,每次扫描到一条边,如果两端的节点,不属于一个集合,就合并两个节点所在的集合,但是我们需要在合并之前计算它们对答案的贡献.

    懒得画图了,抽象地想一下,现在有两个集合X,Y,每个集合内都是一棵树(并查集的性质),然后现在两个集合只有一条边((x,y))相连,现在两个集合要变成一个完全图,根据完全图的定义:"每对不同的顶点之间都恰有一条边相连",贡献就是((size[X]*size[Y]-1)*(w[x][y]+1)),前式表示新建的边的数量,后式是最小长度(为了保证图的最小生成树不变,权值必须要比原图的边大,又为了保证最小,所以大1就好).

    所以我们并查集还要维护每个集合的(size).

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    const int N=6005;
    int fa[N],size[N];
    struct ppx{int x,y,z;}a[N];
    inline bool cmp(ppx x,ppx y){return x.z<y.z;}
    inline int find(int x){
    	if(x==fa[x])return fa[x];
    	return fa[x]=find(fa[x]);
    }
    int main(){
    	int T=read();
    	while(T--){
    		int n=read();
    		for(int i=1;i<n;++i){
    			a[i].x=read();a[i].y=read();a[i].z=read();
    		}
    		for(int i=1;i<=n;++i)fa[i]=i,size[i]=1;
    		sort(a+1,a+n,cmp);
    		int ans=0;
    		for(int i=1;i<n;++i){
    			int x=find(a[i].x),y=find(a[i].y);
    			if(x!=y){
    				ans=ans+(size[x]*size[y]-1)*(a[i].z+1);
    				fa[x]=y;size[y]+=size[x];
    			}
    		}
    		printf("%d
    ",ans);
    	}
        return 0;
    }
    
    
  • 相关阅读:
    Java多线程
    http网页请求状态码
    C++文件读写
    算法训练 最大的算式
    算法训练 2的次幂表示
    线段树- 算法训练 操作格子
    Prim算法(最小生成树)
    Kruskal算法(最小生成树)
    Dijkstra算法(最短路)
    HDU5692 dfs + 线段树维护区间最大值
  • 原文地址:https://www.cnblogs.com/PPXppx/p/11559510.html
Copyright © 2020-2023  润新知