BZOJ 2654: tree
Time Limit: 30 Sec Memory Limit: 512 MB
Submit: 3344 Solved: 1425
[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
题解:
这个题目,我们可以先二分一下给白边所加上的权值,因为我们只需要need条边,所以如果大于need,就需要使mid变大,所以l=mid+1,小于need就相反,但是对答案进行更新的时是在加白边总数大于(tmp)>=mid的时候,因为等于肯定可以,然后考虑如果加上mid tmp>need但加上mid+1, tmp<need的情况,因为我们是在权值相等的情况下优先加入白边,所以可以将一些白边换成黑边.
代码:
/************************************************************** Problem: 2654 User: 1796681012 Language: C++ Result: Accepted Time:3052 ms Memory:3248 kb ****************************************************************/ #include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <cmath> #include <iostream> #define MAXN 100100 using namespace std; int fa[MAXN]; struct edge{ int from,to,quan,id; void read(){ scanf("%d%d%d%d",&from,&to,&quan,&id);from++,to++; } }a[MAXN]; int ans,tmp=0,n,m,need,tot; bool cmp(edge x,edge y){ if(x.quan==y.quan) return x.id<y.id; return x.quan<y.quan; } int find(int now){ if(fa[now]!=now) fa[now]=find(fa[now]); return fa[now]; } void work(){ tmp=0,tot=0;int hh=0; sort(a+1,a+m+1,cmp); for(int i=1;i<=m;i++){ int x=a[i].from,y=a[i].to;int fax=find(x),fay=find(y); if(fax!=fay){ fa[fax]=fay; tot+=a[i].quan;hh++; if(a[i].id==0) tmp++; } if(hh==n-1) break; } } int main() { scanf("%d%d%d",&n,&m,&need); for(int i=1;i<=m;i++) a[i].read(); int l=-105,r=105,mid; while(l<=r){ mid=(l+r)/2; for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=m;i++) if(a[i].id==0) a[i].quan+=mid; work(); if(tmp>=need) ans=tot-need*mid,l=mid+1; else r=mid-1; for(int i=1;i<=m;i++) if(a[i].id==0) a[i].quan-=mid; } printf("%d",ans); return 0; }