BZOJ 2654
2654: tree
Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 2714 Solved: 1114
[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
这道题有点奇怪。。。用控制白边大小的方法使得白边的条数得到控制。
但是并没有找到在重载<运算符时的把白边放在前面的用处,而且不加这一句就会wa。。。
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<cstring> #define N 100000+5 using namespace std; int read() { int x=0,f=1;char s=getchar(); while(s<'0' || s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0' && s<='9'){x=x*10+s-'0';s=getchar();} return x*f; } struct edge{int s,t,c,col;}b[N],a[N]; bool operator < (edge x,edge y){return x.c<y.c || (x.c==y.c && x.col<y.col);} int v,e,need,cnt,tot,ans,l,r,fa[N]; int find(int x) { if(x==fa[x])return x; fa[x]=find(fa[x]); return fa[x]; } int main() { v=read();e=read();need=read(); for(int i=1;i<=e;i++)b[i].s=read()+1,b[i].t=read()+1,b[i].c=read(),b[i].col=read(); l=-100-5,r=100+5; while(l<=r) { int mid=(l+r)>>1,sum=0;tot=0; for(int i=1;i<=v;i++)fa[i]=i; for(int i=1;i<=e;i++) { a[i].s=b[i].s,a[i].t=b[i].t; a[i].c=b[i].c+mid*(1-b[i].col); a[i].col=b[i].col; } sort(a+1,a+e+1); for(int i=1;i<=e;i++) { int p=find(a[i].s),q=find(a[i].t); if(p==q)continue; fa[p]=q; sum+=a[i].c; if(!a[i].col)tot++; } if(tot>=need){ans=sum-need*mid;l=mid+1;} else r=mid-1; } printf("%d\n",ans); return 0; }