WQS二分 Tree I

让我们一起来%forever_shi神犇

题意:
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有k条白色边的生成树。
题解:
看的别人的题解。
做法是二分一个权值,可正可负,让所有白色边加上这个权值,然后再做最小生成树,显然这个全权值是可以二分的。
然后最后每次二分得到的结果再加上那些减去的权值就是这种最小生成树的权值和了。
另外权值相同的时候据说需要先选白色边。

神奇做法,据说可以证明,但我不会啊QAQ,当套路记吧。。。

#include<bits/stdc++.h>
using namespace std;
int n,m,k,f[1001000],tot,res;
struct node
{
    int x,y,c,col;
}e[1001000],a[1001000];
int find(int x)
{
    if(x==f[x])
        return x;
    else
    {
        f[x]=find(f[x]);
        return f[x];
    }
}
bool cmp(node n1,node n2)
{
    if(n1.c!=n2.c)
        return n1.c<n2.c;
    else
        return n1.col<n2.col;
}
bool check(int x)
{
    int numw=0,t=0;
    tot=0;
    for(int i=1;i<=n;++i)
        f[i]=i;
    for(int i=1;i<=m;++i)
        a[i]=e[i];
    for(int i=1;i<=m;++i)
        if(e[i].col==0)
            a[i].c+=x;
    sort(a+1,a+m+1,cmp);
    for(int i=1;i<=m;++i)
    {
        int fx=find(a[i].x);
        int fy=find(a[i].y);
        if(fx!=fy)
        {
            f[fx]=fy;
            if(a[i].col==0)
                numw++;
            tot+=a[i].c;
            t++;
            if(t==n-1)
                break;
        }
    }
    if(numw>=k)
        return true;
    else
        return false;
}
int main()
{
    cin>>n>>m>>k;
    for(int i=1;i<=m;++i)
        scanf("%d%d%d%d",&e[i].x,&e[i].y,&e[i].c,&e[i].col);
    for(int i=1;i<=m;++i)
    {
        e[i].x++;
        e[i].y++;
    }
    int l=-50000,r=50000,ans=0;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(check(mid))
        {
            l=mid+1;
            ans=mid;
            res=tot-ans*k;
        }
        else
         	r=mid-1;
    }
    cout<<res;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/wddwjlss/article/details/83116136
今日推荐