[BZOJ2654] tree
2654: tree
Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 610 Solved: 225
[Submit][Status][Discuss]
Description
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。
题目保证有解。
Input
第一行V,E,need分别表示点数,边数和需要的白色边数。
接下来E行
每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。
Output
一行表示所求生成树的边权和。
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
数据规模和约定
0:V<=10
1,2,3:V<=15
0,..,19:V<=50000,E<=100000
所有数据边权为[1,100]中的正整数。
Source
只想说好巧妙地想法……
对于每条白边,我们每次给他加一个值x(x∈[-101,101],因为所有边权都在这个范围之内),易看出随着x的增大,白边在MST中的数量是渐渐减少的,我们求出每条白边+x时MST中白边的最大数量,因此求出第一个小于need的x,x-1就是我们需要的x,求出此时的MST,即要求的答案。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<vector> #include<queue> using namespace std; struct node { int u,v,w,c; } e[100005]; int u[100005],v[100005],w[100005],c[100005],f[50005]; int n,m,need,ans,sum,cnt,l,r; int find(int x) { return x==f[x]?x:f[x]=find(f[x]); } bool cmp(node a,node b) { return ((a.w<b.w)||((a.w==b.w)&&(a.c<b.c))); } bool pd(int x) { ans=0;cnt=0; for (int i=1;i<=n;i++) f[i]=i; for (int i=1;i<=m;i++) { e[i].u=u[i],e[i].v=v[i],e[i].w=w[i];e[i].c=c[i]; if(!c[i])e[i].w+=x; } sort(e+1,e+m+1,cmp); for (int i=1;i<=m;i++) { int p=find(e[i].u),q=find(e[i].v); if (p!=q) { f[p]=q; ans+=e[i].w; if (e[i].c==0) cnt++; } } return cnt>=need; } int main() { scanf("%d%d%d",&n,&m,&need); for (int i=1;i<=m;i++) { scanf("%d%d%d%d",&u[i],&v[i],&w[i],&c[i]); u[i]++; v[i]++; } l=-101; r=101; while (l<=r) { int mid=(l+r)/2; if (pd(mid)) { l=mid+1; sum=ans-need*mid; } else r=mid-1; } printf("%d",sum); }