【vijos】【生成樹】最小生成樹的最小完全圖

描述

最小生成樹P.S.S在宿命的指引下找到了巫師Kismi。P.S.S希望Kismi能幫自己變成一個完全圖。Kismi由於某些不可告人的原因,把這件事交給了你。
PS: 可以保證,這個最小生成樹對於最後求出的完全圖是唯一的。
格式

輸入格式

輸入的第一行是一個整數n,表示生成樹的節點數。
接下來有n-1行,每行有三個正整數,依次表示每條邊的端點編號和邊權。
(頂點的邊號在1-n之間,邊權

輸出格式

一個整數ans,表示以該樹爲最小生成樹的最小完全圖的邊權之和。

分析

根據Kruskal算法,將已有的最小生成樹的邊權從小到大排序,依次考察。當前邊在添加之前,一定是連接兩點所在集合的邊權最小的邊。所以兩個集合之間的其他邊的邊權至少都是該邊邊權+1.

問題就轉化成了,怎樣快速判斷該邊兩點所在並查集的大小?在並查集合並時以根節點爲下標記錄並查集大小即可。

int fa[maxn],f[maxn]; 
int find(int a){
    fa[a]==a ? a : fa[a]=find(fa[a]);
}
void merge(int a,int b){
    int x=find(a),y=find(b);
    f[b]+=f[a];
    fa[x]=y;
}

代碼

#include <cstdio>
#include <algorithm>
#include <cstring>
#define maxn 21000
using namespace std;

long long fa[maxn],f[maxn],n;

int find(int a){
    return fa[a]==a ? a : fa[a]=find(fa[a]);
}

void merge(int a,int b){ 
    long long x=find(a),y=find(b);
    f[y]+=f[x];
    fa[x]=y;
}

struct edge{
    long long left,right,val;
}g[maxn];

bool operator < (const edge &e1,const edge &e2){
    return e1.val<e2.val;
}

long long ans=0;

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n-1;i++){
        int a,b,c;
        scanf("%lld%lld%lld",&g[i].left,&g[i].right,&g[i].val);
        ans+=g[i].val;
    }
    sort(g+1,g+n);
    for(int i=1;i<=n;i++) {
        fa[i]=i; f[i]=1;
    }
    for(int i=1;i<=n-1;i++){
        int a=find(g[i].left);
        int b=find(g[i].right),c=g[i].val;
        ans+=(f[a]*f[b]-1)*(c+1);
        merge(g[i].left,g[i].right);
    }
    printf("%lld",ans);
    return 0;
}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章