【国家集训队2012】【BZOJ2654】tree

来源:互联网 发布:linux vi中替换字符串 编辑:程序博客网 时间:2024/05/21 02:35

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]中的正整数。

Source
CLJ当年互测出的题…
其实是傻逼题
考虑二分一个值,给白边边权加上这个值,然后不论颜色MST,记录白边数量
一定有一个值满足MST中白边数量恰好为k
二分姿势不对一直WA(╯‵□′)╯︵┻━┻

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#define MAXN 50100using namespace std;int n,m,k;int f[MAXN];int sum,cnt,tot,ans;void in(int &x){    char ch=getchar();x=0;int flag=1;    while (!(ch>='0'&&ch<='9')) flag=ch=='-'?-1:flag,ch=getchar();    while (ch>='0'&&ch<='9')    x=x*10+ch-'0',ch=getchar();x*=flag;}int find(int x){    return f[x]==x?x:f[x]=find(f[x]);}void Union(int x,int y){     int a=find(x),b=find(y);    f[a]=b;}struct edge{    int u,v,w,c;    bool operator <(const edge& a)const    {        return w==a.w?c<a.c:w<a.w;    }}e[MAXN<<1];inline void kruskal(){    sum=0;tot=0;cnt=0;    for (int i=1;i<=n+1;i++)    f[i]=i;    sort(e+1,e+m+1);    for (int i=1;i<=m;i++)    {        if (tot>=n-1)   break;        if (find(e[i].u)!=find(e[i].v))        {            Union(e[i].u,e[i].v);            tot++;cnt+=(!e[i].c);sum+=e[i].w;        }    }}int main(){    in(n);in(m);in(k);    for (int i=1;i<=m;i++)  in(e[i].u),in(e[i].v),in(e[i].w),in(e[i].c),e[i].u++,e[i].v++;    int l=-101,r=101,mid=0;    while (l<=r)    {        mid=(l+r)>>1;        for (int i=1;i<=m;i++)  e[i].w+=(!e[i].c)*mid;        kruskal();        if (cnt>=k) l=mid+1,ans=sum-k*mid;        else    r=mid-1;        for (int i=1;i<=m;i++)  e[i].w-=(!e[i].c)*mid;    }    printf("%d\n",ans);}
0 0