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 1
0 1 1 1
0 1 2 0
0 1 1 1
0 1 2 0
Sample Output
2
二分+最小生成树,一开始真的很难想。就是二分一个值,给所有的白色边加上这个值,跑最小生成树保证白边数刚好是need
其中二分中的细节需要注意一下。
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <cmath> #include <cstdlib> #include <queue> #include <stack> #include <vector> using namespace std; #define MAXN 100010 #define INF 2147483647 #define MOD 10000007 #define LL long long #define in(a) a=read() #define REP(i,k,n) for(int i=k;i<=n;i++) #define DREP(i,k,n) for(int i=k;i>=n;i--) #define cl(a) memset(a,0,sizeof(a)) inline int read(){ int x=0,f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x*f; } inline void out(int x){ if(x<0) putchar('-'),x=-x; if(x>9) out(x/10); putchar(x%10+'0'); } int n,m,need; int sum,num; int ans; int f[MAXN]; int a[MAXN],b[MAXN],w[MAXN],c[MAXN]; struct node{ int a,b,w,c; }l[MAXN]; int getf(int k){ if(f[k]==k) return k; return f[k]=getf(f[k]); } bool cmp(node x,node y){ if(x.w==y.w) return x.c<y.c; return x.w<y.w; } bool check(int k){ sum=0; num=0; REP(i,1,n) f[i]=i; REP(i,1,m){ l[i].a=a[i]; l[i].b=b[i]; l[i].c=c[i]; l[i].w=w[i]; if(l[i].c==0) l[i].w+=k; } sort(l+1,l+m+1,cmp); for(int i=1;i<=m;i++){ int x=getf(l[i].a),y=getf(l[i].b); if(x!=y){ f[y]=x; sum+=l[i].w; if(!l[i].c) num++; } } if(num>=need) return true; return false; } int main(){ in(n);in(m);in(need); REP(i,1,m){ in(a[i]);in(b[i]);in(w[i]);in(c[i]); a[i]++; b[i]++; } int left=-10010,right=10010; while(left<=right){ int mid=(right+left)>>1; if(check(mid)){ left=mid+1; ans=sum-mid*need; } else right=mid-1; } cout<<ans; return 0; }