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

版权声明:转载请注明原出处啦QAQ(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/83108132

题目: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;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/83108132
今日推荐