蓝书(算法竞赛进阶指南)刷题记录——CH6201 走廊泼水节(最小生成树)

题目:CH6201.
题目大意:给定一棵 n n n个点的树,让你扩充成一张完全图,使得原树是这张完全图的唯一最小生成树,并输出加的边的最小边权和.
1 ≤ n ≤ 6000 1\leq n\leq 6000 1n6000,边权 ≤ 100 \leq 100 100,数据组数 ≤ 10 \leq 10 10.

这道题用了一个类似于Kruskal的东西,然后顺便计算出了最小边权和.

首先,我们将树拆开,将边排序,然后不断用并查集合并.

每合并一次,我们设合并的两个联通块为 x x x y y y,那么合并时就会 x x x y y y之间除了已有的这条边本身,其它边的边权都要大于已有的这条边.

而且我们可以发现,其它边的限制条件肯定只有大于这条边和比这条边小的树边,所以我们发现其它边的边权只要设成这条边的边权 + 1 +1 +1即可.

那么我们就kruskal的同时,每当加入一条边,就将答案加上其它的边的边权,我们发现其它边的数量即为 x x x的大小乘上 y y y的大小减 1 1 1,边权都为这条边的边权加 1 1 1.写成公式即为:
a n s = a n s + ( x . s i z e ∗ y . s i z e − 1 ) ∗ ( v [ i ] + 1 ) . ans=ans+(x.size*y.size-1)*(v[i]+1). ans=ans+(x.sizey.size1)(v[i]+1).

代码如下:

#include
  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;
}

你可能感兴趣的:(蓝书(算法竞赛进阶指南)刷题记录——CH6201 走廊泼水节(最小生成树))