Tree

https://loj.ac/problem/10069

题目描述

  给出一张图,每条边除边权外还有颜色(黑白两色),求最小权的恰有(need)条白边的生成树。

思路

  直接求最小生成树再不断删边和加边使得生成树恰有(need)条边且仍是最小权很难维护,我们可以考虑把所有白边都加上一个值,再求一遍最小生成树,可以证明这样必定可以有一种方案可以使一种求出的最小生成树恰有(need)条白边,这样就实现对白边数量的调节,而对于这个值我们可以进行二分。

代码

#include <bits/stdc++.h>
using namespace std;
const int MAXN=5e4+10,MAXM=1e5+10;
struct Edge
{
    int s,t,c,col;
}e[MAXM];
int fa[MAXN],m,sum,n,need;
bool cmp(Edge x,Edge y)
{    
    if(x.c==y.c)return x.col<y.col;
    else return x.c<y.c;
}
int find(int x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
bool check(int k)
{
    for(int i=1;i<=m;i++)
        if(!e[i].col)e[i].c+=k;
    for(int i=0;i<n;i++)
        fa[i]=i;
    sort(e+1,e+m+1,cmp);
//    for(int i=1;i<=m;i++)
//        printf("%d %d
",i,e[i].c);
    int cnt=0,s=0;
    sum=0;
    for(int i=1;i<=m;i++)
    {
        int fx=find(e[i].s),fy=find(e[i].t);
        if(fx!=fy)
        {
            fa[fx]=fy;
            cnt++;
            sum+=e[i].c;
            if(!e[i].col)s++;
            if(cnt==n-1)break ;
        }
    }
//    cout<<k<<' '<<sum<<' '<<s<<endl;
    for(int i=1;i<=m;i++)
        if(!e[i].col)e[i].c-=k;
    return s>=need;
}
int main() 
{
    scanf("%d%d%d",&n,&m,&need);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d%d",&e[i].s,&e[i].t,&e[i].c,&e[i].col);
    int l=-10000,r=10000,ans;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(check(mid))
        {
            ans=sum-need*mid;
            l=mid+1;
        }
        else r=mid-1;
//        cout<<sum<<endl;
    }
    printf("%d",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/fangbozhen/p/11794894.html