1. 程式人生 > >藍書(演算法競賽進階指南)刷題記錄——CH6201 走廊潑水節(最小生成樹)

藍書(演算法競賽進階指南)刷題記錄——CH6201 走廊潑水節(最小生成樹)

題目:CH6201.

題目大意:給定一棵樹,讓你擴充成一張完全圖,使得原樹是這張完全圖的唯一最小生成樹,並輸出加的邊的最小邊權和.

這道題用了一個類似於Kruskal的東西,然後順便計算出了最小邊權和.

首先,我們將樹拆開,將邊排序,然後不斷用並查集合並.

每合併一次,我們設合併的兩個聯通塊為x和y,那麼合併時就會x和y之間除了已有的這條邊本身,其它邊的邊權都要大於已有的這條邊.

而且我們可以發現,其它邊的限制條件肯定只有大於這條邊和比這條邊小的樹邊,所以我們發現其它邊的邊權只要設成這條邊的邊權+1即可.

那麼我們就kruskal的同時,每當加入一條邊,就將答案加上其它的邊的邊權,我們發現其它邊的數量即為x的大小乘上y的大小減1,邊權都為這條邊的邊權加1.寫成公式即為:

ans=ans+(x.size*y.size-1)*(v[i]+1).

程式碼如下:

#include<bits/stdc++.h>
  using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=6000;
struct side{
  int x,y,v;
  bool operator < (const side p)const{return v<p.v;}
}e[N+9];
int ans,n;
int fa[N+9],cnt[N+9];
int get(int u){return fa[u]=u^fa[u]?get(fa[u]):u;}
Abigail into(){
  scanf("%d",&n);
  for (int i=1;i<n;i++)
    scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].v);
}
Abigail work(){
  ans=0;
  for (int i=1;i<=n;i++) fa[i]=i,cnt[i]=1;
  stable_sort(e+1,e+n);
  for (int i=1;i<n;i++){
    ans+=(e[i].v+1)*(cnt[get(e[i].x)]*cnt[get(e[i].y)]-1);
    cnt[get(e[i].x)]+=cnt[get(e[i].y)];
    fa[get(e[i].y)]=get(e[i].x);
  }
}
Abigail outo(){
  printf("%d\n",ans);
}
int main(){
  int T;
  scanf("%d",&T);
  while (T--){
    into();
    work();
    outo();
  }
  return 0;
}