BZOJ 2654: tree Kruskal+二分答案
2654: tree
Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 1863 Solved: 736
[Submit][Status][Discuss]
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 1
0 1 1 1
0 1 2 0
0 1 1 1
0 1 2 0
Sample Output
2
HINT
原数据出错,现已更新 by liutian,但未重测---2016.6.24
Source
想法:给全部白边加上同一个数,可以让白边在Kruskal中的加入顺序平移一下,那么白边数量也随之改变。
边权比较小,可以暴力枚举这个数O(200*m).发现这个数越大,白边数越少,于是二分优化。O(m*log200)
#include<cstdio> #include<algorithm> const int len(50000),lem(100000),INF(0x7fffffff); int V,E,need,a,b,c,col,f[len+10],limt,total,bf,ans,sum,last; struct Node{int a,b,c;}white[lem+10],black[lem+10];int Wt,Bt; bool cmp(Node A,Node B){return A.c<B.c;} template <class T>void read(T &x) { x=0;char ch=getchar();int f=0; while(ch<'0'||ch>'9'){f=(ch=='-');ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} x=f?-x:x; } int gf(int x){return x==f[x]?x:f[x]=gf(f[x]);} bool union_fa(int x,int y) { x=gf(x),y=gf(y); f[x]=y; return (x!=y); } void Kruskal() { total=sum=0; int Wl=1,Bl=1; for(int i=1;i<=V;i++)f[i]=i; while(Wl<=Wt || Bl<=Bt) { if(Wl<=Wt && (white[Wl].c+limt<=black[Bl].c || Bl>Bt)) { if(union_fa(white[Wl].a,white[Wl].b))total++,sum+=white[Wl].c; Wl++; }else { if(union_fa(black[Bl].a,black[Bl].b))sum+=black[Bl].c; Bl++; } } } int main() { // freopen("C.in","r",stdin); // freopen("C.out","w",stdout); read(V),read(E),read(need); for(int i=1;i<=E;i++) { read(a),read(b),read(c),read(col);a++,b++; if(col)black[++Bt]=(Node){a,b,c}; else white[++Wt]=(Node){a,b,c}; } std::sort(white+1,white+1+Wt,cmp); std::sort(black+1,black+1+Bt,cmp); limt=0; for(int l=-105,r=105;l<=r;) { limt=(l+r)/2; Kruskal(); if(total>=need)ans=sum,l=limt+1;else r=limt-1; } printf("%d",ans); return 0; }