【BZOJ2654】tree 二分+最小生成樹
阿新 • • 發佈:2017-07-16
con 連通圖 顏色 kruskal bool cpp || esp 答案
0 1 1 1
0 1 2 0
【BZOJ2654】tree
Description
給你一個無向帶權連通圖,每條邊是黑色或白色。讓你求一棵最小權的恰好有need條白色邊的生成樹。 題目保證有解。Input
第一行V,E,need分別表示點數,邊數和需要的白色邊數。 接下來E行,每行s,t,c,col表示這邊的端點(點從0開始標號),邊權,顏色(0白色1黑色)。Output
一行表示所求生成樹的邊權和。 V<=50000,E<=100000,所有數據邊權為[1,100]中的正整數。Sample Input
2 2 10 1 1 1
0 1 2 0
Sample Output
2題解 :又是一種奇奇怪怪的做法~
如果我們給所有白色邊增加邊權,那麽所選的白色邊一定越來越少(反之同理)。所以我們二分給白色邊增加多少邊權,跑kruskal,最後再將增加的邊權減去即可。
但是你可能懷疑二分的正確性?即如果給白色邊邊權加上mid,則所選白色邊>need,如果加上mid+1,則所選白色邊<need。解決方法是,在排序的時候,我們將白色邊放在相同長度的黑色邊之前。這樣,因為mid+1時白邊<mid,所以一定有若幹=mid的黑邊。在mid時,我們多選的白邊就可以被黑邊替換掉。所以在最後統計答案的時候,只需要ans-=mid*need即可。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; int n,m,nd,ans,sum,cnt,wt; struct edge { int a,b,col,val; }p[100010]; int f[50010]; int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘)f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+gc-‘0‘,gc=getchar(); return ret*f; } bool cmp(edge a,edge b) { return (a.val==b.val)?(a.col<b.col):(a.val<b.val); } int find(int x) { return (f[x]==x)?x:(f[x]=find(f[x])); } int solve(int x) { int i,ra,rb,ret; for(i=1;i<=m;i++) if(!p[i].col) p[i].val+=x; sort(p+1,p+m+1,cmp); sum=cnt=wt=0; for(i=1;i<=n;i++) f[i]=i; for(i=1;i<=m;i++) { ra=find(p[i].a),rb=find(p[i].b); if(ra!=rb) { cnt++,wt+=1-p[i].col,f[ra]=rb,sum+=p[i].val; if(cnt==n-1) { if(wt>=nd) ans=sum-x*nd,ret=1; else ret=0; } } } for(i=1;i<=m;i++) if(!p[i].col) p[i].val-=x; return ret; } int main() { int i,l=0,r=0,mid; n=rd(),m=rd(),nd=rd(); for(i=1;i<=m;i++) p[i].a=rd()+1,p[i].b=rd()+1,p[i].val=rd(),p[i].col=rd(),r=max(r,p[i].val+1); l=-r; while(l<r) { mid=l+r>>1; if(solve(mid)) l=mid+1; else r=mid; } printf("%d",ans); return 0; }
【BZOJ2654】tree 二分+最小生成樹