NAIPC2017 E - Blazing New Trails (二分 + 最小生成树)

题目链接

给出一个图,对于其中一些确定的边,可以将它们的权值都加上某一个值,使得这些边中正好有w条出现在最小生成树中。求最小生成树的最小总权值。

可以发现,对于加到特殊边上的值,它和最小生成树中特殊边的数量是一个单调的关系。因此可以二分这个值,然后每次去求最小生成树。

实现起来主要是一些细节的问题。在合法的解中,这个值与最小生成树的值也是一个单调的关系,可以相应的简化一下记录的过程。

以及这个东西完全没有必要用double去做,反而会产生某些精度的问题。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;
typedef long long ll;
const ll INF = 0x3f3f3f3f;
const int maxn = 200050;
const int maxm = 500050;

int n, m, x, k, w, cnt, num;
ll res;
int pre[maxn], flag[maxn];
struct node
{
    int u, v;
    ll w;
    int vis;
}e[maxm];

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

int Find(int x)
{
    if(x == pre[x]) return x;
    return pre[x] = Find(pre[x]);
}

void kruskal(ll x)
{
    for(int i = 1;i <= m;i++)
    {
        if(e[i].vis == 1)
            e[i].w = e[i].w + x;
    }
    for(int i = 0;i <= n;i++) pre[i] = i;
    sort(e+1, e+m+1, cmp);
    cnt = num = res = 0;
    for(int i = 1;i <= m;i++)
    {
        int fu = Find(e[i].u);
        int fv = Find(e[i].v);
        if(fu != fv)
        {
            if(e[i].vis == 1) num++;
            pre[fu] = fv;
            cnt++;
            res += e[i].w;
            if(cnt >= n-1) break;
        }
    }
    for(int i = 1;i <= m;i++)
    {
        if(e[i].vis == 1)
            e[i].w = e[i].w - x;
    }
    if(cnt != n-1) num = -1;
}

int main()
{
    scanf("%d%d%d%d", &n, &m, &k, &w);
    memset(flag, 0, sizeof(flag));
    for(int i = 1;i <= k;i++)
    {
        scanf("%d", &x);
        flag[x] = 1;
    }
    int cnt = 0;
    for(int i = 1;i <= m;i++)
    {
        scanf("%d%d%lld", &e[i].u, &e[i].v, &e[i].w);
        if(flag[e[i].u] + flag[e[i].v] == 1)
            e[i].vis = 1, cnt++;
        else e[i].vis = 0;
    }
    if(cnt < w || m < n-1) {puts("-1"); return 0;}
    ll L = -INF, R = INF, ans = -1;
    while(L <= R)
    {
        ll mid = (L + R)/2;
        kruskal(mid);
        if(num < 0) {puts("-1"); return 0;}
        if(num >= w) ans = res - mid*w, L = mid + 1;
        else R = mid - 1;
    }
    printf("%lld\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/NPU_SXY/article/details/83098304