【bzoj2654】tree

来源:互联网 发布:企业微观数据库 编辑:程序博客网 时间:2024/06/10 10:29

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
Sample Output

2
HINT

数据规模和约定
0:V<=10
1,2,3:V<=15
0,..,19:V<=50000,E<=100000
所有数据边权为[1,100]中的正整数。

题解
二分,随着白色边权的值增加,白色边出现在生成树里面的个数减少,有单调性,所以可以二分白色边权的增加量,计算白色边出现次数。

代码

#include<bits/stdc++.h> #define N 500005#define ll long long#define inf 100000000#define mod 100000000using namespace std;inline int read(){    int x=0;char ch=getchar();    while (ch<'0'||ch>'9') ch=getchar();    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();    return x;}int f[1000001],n,m,need,tmp,ans,cnt,ans2;  struct node{int st,ed,c,v;}a[100010];  int find(int i){    if (f[i]!=i) f[i]=find(f[i]);    return f[i];}bool cmp(node a,node b){if (a.v==b.v) return a.c<b.c;else return a.v<b.v;}  void solve(){    sort(a+1,a+m+1,cmp);    for (int i=1;cnt!=n-1;i++)    {          int r1=find(a[i].st),r2=find(a[i].ed);          if (r1!=r2){f[r1]=r2;cnt++;if (a[i].c==0) tmp++;ans+=a[i].v;}      }}int main(){    n=read();m=read();need=read();    for (int i=1;i<=m;i++)    {        a[i].st=read();a[i].ed=read();a[i].v=read();a[i].c=read();        a[i].st++;a[i].ed++;     }    int l=-105,r=105;    while (l<=r)    {        int mid=(l+r)>>1;        for (int i=1;i<=m;i++) if (a[i].c==0) a[i].v+=mid;        for (int i=1;i<=n;i++) f[i]=i;        ans=tmp=cnt=0;        solve();        if (tmp>=need){l=mid+1;ans2=ans-need*mid;}else r=mid-1;        for (int i=1;i<=m;i++) if (a[i].c==0) a[i].v-=mid;    }    printf("%d",ans2);    return 0;}
原创粉丝点击