jdoj1574-vijos1579 宿命的PSS
题目大意:给你一棵n个点的最小生成树。求:由这个最小生成树所构成的完全图的边权和的最小值且保证这个完全图的最小生成树是题目给定的且唯一的。
注释:n<20000,边权<=int。
想法:这时博主迄今做的坠难的图论题了(我还是太菜了.....)。我们思考最小生成树的生成过程(spfa这么鬼畜的算法就不要了吧),kruskal。这个最小生成树的生成过程是将所有边排序,对于当前松弛到的边进行判断,用并查集的方式判断这两个点是否在同一个联通块里。那么,如果这条边满足题意,那么这条边的作用是什么?就是连接两个本不连通的联通块。那么,我们考虑,这两个联通块一定需要且仅需要一条边将它们相连。如果如果存在这样的一条边使得它的两个顶点横跨两个联通块且这条边的边权小于题目给出的边,那么这条边显然可以替代题目中给出的边,与题意矛盾。所以,我们需要将所有横跨两个联通块之间的边的边权设置为大于刚刚松弛到的边权,即,为当前边权+1即可。我们需要记录每个联通块的点的个数,以及并查集合并。乘的时候我们可以将所有横跨两个联通块的边先暂时都记为松弛到的边权+1,最后在减去(n-1),即可。
最后,附上丑陋的代码... ...
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 typedef long long ll; 5 using namespace std; 6 struct Node 7 { 8 int a; 9 int b; 10 ll val; 11 }f[100100]; 12 ll size[20010];//由于边权的范围,且最后又乘法的出现,我们将其设为ll。 13 int fa[20010]; 14 bool cmp(Node a,Node b) 15 { 16 return a.val==b.val?a.a<b.a:a.val<b.val; 17 } 18 int find(int x) 19 { 20 return fa[x]==x?x:(fa[x]=find(fa[x])); 21 } 22 void fix(int x,int y) 23 { 24 x=find(x),y=find(y); 25 fa[x]=y,size[y]+=size[x]; 26 } 27 int main() 28 { 29 ll ans=0;//一定要开ll。 30 int n; 31 scanf("%d",&n); 32 for(int i=1;i<=n-1;i++) 33 { 34 scanf("%d%d%lld",&f[i].a,&f[i].b,&f[i].val); 35 } 36 for(int i=1;i<=n;i++) fa[i]=i,size[i]=1; 37 sort(f+1,f+n+1,cmp); 38 for(int i=1;i<=n;i++)//松弛。 39 if(find(f[i].a)!=find(f[i].b))//判断这个边是否满足题意。 40 { 41 ans+=size[find(f[i].a)]*size[find(f[i].b)]*(f[i].val+1); 42 fix(f[i].a,f[i].b); 43 } 44 ans-=(n-1);//别忘了,我们在计算的时候是将应该是f[i].val的边算成了f[i].val+1,所以,应该减去。 45 printf("%lld\n",ans); 46 return 0; 47 }
小结,别忘了开long long ,别忘了开long long ,别忘了开long long !!