HDU 4253 二分 + 最小生成树

题意:
题意是给出n个点,m条边其中有的属于A有的属于B,要求构造最小生成树 并且 包含k条A的边 求最小花费

思路:这题思路有点nb 我们二分一个权值给属于A公司的边加上 我们知道kruskal需要按照权值排序 那么属于A的边一定会靠后点 这样跑一遍kruskal 看看构造出来的是否满足有k条属于A的边 如果不满足那么权值下调 r = mid 否则 权值上调 l = mid 这样单调性就出来了 注意给边加上权值后记得还原 答案的话因为你多加了k个mid权值 k是边数 mid是你二分出来的权值 记得减去

#include 

using namespace std;

const int N = 50005,M = 100005;

struct node
{
    int u,v,w,id;
} a[M];

int n, m, k, ans, res;

int fa[N];

void init()
{
    for(int i = 0;i <= n;i ++)
        fa[i] = i;
}

int findx(int x)
{
    if(fa[x] == x)
        return x;
    return fa[x] = findx(fa[x]);
}


bool cmp(node a,node b)
{
    if(a.w != b.w)
        return a.w < b.w;
    return a.id < b.id;
}

bool kruskal(int x)
{
    for(int i = 0; i < m; i ++)
    {
        if(a[i].id == 0)
            a[i].w += x;
    }

    init();

    sort(a,a + m,cmp);

    int cnt = 0;
    int sum = n - 1;
    res = 0;

    for(int i = 0; i < m; i ++)
    {
        int x = findx(a[i].u);
        int y = findx(a[i].v);

        if(x != y)
        {
            fa[x] = y;

            res += a[i].w;

            sum -= a[i].id;

            cnt ++ ;
        }

        if(cnt == n - 1)
            break;
    }

    for(int i = 0; i < m; i ++)
    {
        if(a[i].id == 0)
            a[i].w -= x;
    }

    return sum >= k;
}

int main()
{
    int cas = 1;

    while(~scanf("%d%d%d",&n,&m,&k))
    {
        ans = 0x3f3f3f3f;

        for(int i = 0; i < m; i ++)
            scanf("%d%d%d%d",&a[i].u,&a[i].v,&a[i].w,&a[i].id);

        int l = -100,r = 100,mid ;

        while(l + 1< r)
        {
            mid = l + r >> 1;

            if(kruskal(mid))
                l = mid, ans = res - mid * k;
            else
                r = mid;
        }

        printf("Case %d: %d\n",cas ++,ans);
    }
    return 0;
}

你可能感兴趣的:(图论)