标签:span its 二分 最小生成树 name tps col lse scan
参考资料:https://blog.csdn.net/sunshinezff/article/details/48749453
题解摘抄:
显然可以发现随着白边权值的增大。最小生成树中白边的个数不增。
然后根据这个性质我们就可以二分一个值,然后每次给白边加上这个值。看一下最小生成树中白边的个数。
最后答案再把它减去。
看起来思路非常简单,但是有一个很重要的细节。
如果在你的二分过程中如果给白边加上mid,你得到的白边数比need大。
给白边加上mid+1,你得到的白边比need小。
这种情况看似没法处理。
但是考虑一下克鲁斯卡尔的加边顺序。
可以发现如果出现这种情况,一定是有很多相等的白边和黑边。因为数据保证合法。
所以我们可以把一些白边替换成黑边。
所以我们要在白边数>=need的时候跟新答案。
具体用ans=ans-mid*need;即可。
#include<bits/stdc++.h> using namespace std; struct node{ int x,y,w,c; }a[1100000]; int pre[6000000]; int v,e,need,s[6000000],t[6000000],c[6000000],col[6000000],m=0; int find(int x){return x==pre[x]?x:pre[x]=find(pre[x]); } bool cmp(node a,node b){ if(a.w==b.w) return a.c<b.c;//关键点 else return a.w<b.w; } int ans,tot; int kruskal(int mid) { int num=0; memset(a,0,sizeof a); for(int i=1;i<=e;i++) { a[i].x=s[i]; a[i].y=t[i]; a[i].c=col[i]; a[i].w=c[i]; if(a[i].c==0) { a[i].w+=mid; } } stable_sort(a+1,a+e+1,cmp); for(int i=0;i<=v+1;i++) pre[i]=i;//注意点 int cnt=0; tot=0; for(int i=1;i<=e;i++) { int fx=find(a[i].x); int fy=find(a[i].y); if(fx!=fy){ cnt++; tot+=a[i].w; pre[fx]=fy; if(a[i].c==0) { num++; } if(cnt==v-1) break; } } return num; } int main() { scanf("%d%d%d",&v,&e,&need); for(int i=1;i<=e;i++) { scanf("%d%d%d%d",&s[i],&t[i],&c[i],&col[i]); s[i]++;t[i]++; } int l=-155555,r=155555;//注意点三 while(l<=r) { int mid=(l+r)/2; int a1=kruskal(mid); if(a1>=need) { l=mid+1; ans=tot-mid*need;//关键点 } else r=mid-1; } cout<<ans; return 0; }
标签:span its 二分 最小生成树 name tps col lse scan
原文地址:https://www.cnblogs.com/719666a/p/9534804.html