AcWing 346. 走廊泼水节
考察:最小生成树
思路:
本题要求完成图的最小生成树依旧是原树.考虑Kruskal算法,每次都是选择当前边两端合并为一个集合,我们要保证为完全图的话需要让左右端点的集合两两之间连一条边,同时保证原树的边是当前集合最小的边.因为原边不能代替所以考虑取road[i].w+1的边,边的数量是左端点集合数*右端点集合数-1.
每次合并集合都构造完全图,这样合并完成后一定是完全图.
按照上面求ans的话,如果边从大到小枚举会使得答案更小,但是这样的答案是不合法的,这样会使完成图的最小生成树比原树更小.
1 #include <iostream> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 const int N = 6010; 6 int p[N],n,sz[N]; 7 struct Road{ 8 int u,v,w; 9 bool operator<(const Road& r)const{ 10 return this->w<r.w; 11 } 12 }road[N]; 13 int findf(int x) 14 { 15 if(x!=p[x]) p[x] = findf(p[x]); 16 return p[x]; 17 } 18 int main() 19 { 20 int T; 21 scanf("%d",&T); 22 while(T--) 23 { 24 scanf("%d",&n); 25 for(int i=1;i<=n;i++) p[i] = i,sz[i] = 1; 26 for(int i=1;i<n;i++) 27 { 28 int a,b,w; scanf("%d%d%d",&a,&b,&w); 29 road[i] = {a,b,w}; 30 } 31 sort(road+1,road+n); 32 int sum = 0; 33 for(int i=1;i<n;i++) 34 { 35 int pa = findf(road[i].u),pb = findf(road[i].v); 36 if(pa==pb) continue; 37 sum += (sz[pb]*sz[pa]-1)*(road[i].w+1); 38 sz[pb]+=sz[pa]; 39 p[pa] = pb; 40 } 41 printf("%d\n",sum); 42 } 43 return 0; 44 }