[BZOJ 2654]tree(陈立杰)

[BZOJ 2654]tree(陈立杰)

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

V<=50000,E<=100000,所有数据边权为[1,100]中的正整数。

题解:
一道思维题,代码打起来实际上很简单。

 

二分+kruskal
如果直接kruskal求最小生成树,是无法保证白边数量的,那么我们考虑如果改变白边的数量。我们可以把白边全部都加上一个权值,也就是我们二分的值,然后跑最小生成树,同时记录白边数量。当白边数量>=need时,l=mid+1,否则r=mid−1,更新答案就是这棵生成树的权值和减去所有白边的增量。
证明:
我们发现,如果我们给白边增加权值,做最小生成树,由于白边权值增大,导致不容易选白边。记f(x)为给白边增加x(x可为负)权值,做最小生成树后,选白边的数量。可以发现,f(x)随x增大而减小,显然可以二分。
其次,我们发现,由于黑边的权值是不变的,与白边权值不相互影响。同样由于白边之间关系相对不变,必然选出的need条白边一定是符合题意的。

注意排序的时候如果权值相同要把白色的放在前面。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m,s,ans,mmin;
struct node
{
    int from,to,dis,color;
}edge[100001];
bool cmp(const node a,const node b){if(a.dis!=b.dis)return a.dis<b.dis;else return a.color<b.color;}
void change(int mid)
{
    int i;
    for(i=1;i<=m;i++)
    if(!edge[i].color)edge[i].dis+=mid;
}
void change_back(int mid)
{
    int i;
    for(i=1;i<=m;i++)
    if(!edge[i].color)edge[i].dis-=mid;
}
int father[50001];
int find(int x)
{
    if(father[x]==x)return x;
    else return father[x]=find(father[x]);
}
bool judge(int mid)
{
    int i,c=0;
    mmin=0;
    change(mid);
    sort(edge+1,edge+m+1,cmp);
    for(i=0;i<=n;i++)father[i]=i;
    for(i=1;i<=m;i++)
    {
        int p=find(edge[i].from),q=find(edge[i].to);
        if(p!=q)
        {
            father[p]=q;
            mmin+=edge[i].dis;
            if(edge[i].color==0)c++;
        }
    }
    change_back(mid);
    return c>=s;
}
int main()
{
    int i,j;
    scanf("%d%d%d",&n,&m,&s);
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d%d",&edge[i].from,&edge[i].to,&edge[i].dis,&edge[i].color);
    }
    int l=-100,r=100,mid;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(judge(mid))ans=mmin-mid*s,l=mid+1;
        else r=mid-1;
    }
    printf("%d",ans);
    return 0;
}

 

 

posted @ 2017-08-15 20:37  kakakakakaka  阅读(322)  评论(0编辑  收藏  举报

Never forget why you start

//鼠标爆炸特效