P5633 最小度限制生成树 题解

代码写的可能比较繁琐,请见谅。

思路#

首先考虑 wqs 二分。

对所有连向 s 的边进行定量偏移。

即所有连向 s 的边的边权 q 加上二分的定量 Δ

就可以将这一个问题变成一个可行性问题。

可能比较困难的一点就是当 Δ 一定时,可能拥有不同的 k 的方案。

本篇主要讲述如何构造一个正确的方案。

首先判断一个无解的情况。

  1. s 的出边不足 k
  2. 原图不联通。
  3. 使原图联通 s 必须连的出边超过 k

除去上面三种情况,所有的情况必然会有一个解。

考虑对于一个偏移量如何构造出这样的一个解。

首先可以构造出一组 s 出边最少的解。

考虑在此基础上将其增加至 k 条出边。

对于相同权值的边,我们先将原方案中所连的出边进行连接。

然后如果不足 k 条出边,再去考虑相同权值下的其他出边。

最后再去考虑相同权值下的其他边。

可以发现,原方案中所连的出边一定还会在新方案中被连。

这是由于虽然我们增加了一些边,但对于原方案中所连的出边所更改的联通性是不会进行改变的(不然就会出现在原方案中)。

容易发现这样也一定不会比原方案更劣(原理大致与朴素的 kruskal 差不多),并且一定有解。

这样就有了一个大致的算法流程。

具体可以看代码。

Code#

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 1000010;

int n, m, k, s, kl, top, fa[N], vis[N], ans[N];

class Graph
{
public:
    int cnt;
    struct edge
    {
        int to, nxt, val, id;
    } e[N];
    inline edge &operator[](int tmp) { return e[tmp]; }
    inline void init(int x)
    {
        for (int i = 1; i <= cnt; i++)
            e[i].val += x;
    }
    inline void add(int x, int y, int z, int i) { e[++cnt] = {x, y, z, i}; }
    inline void mySort()
    {
        sort(e + 1, e + cnt + 1, [](edge a, edge b)
             { return a.val < b.val; });
    }
} graBlack, graWhite;

inline int read()
{
    int asd = 0, qwe = 1;
    char zxc;
    while (!isdigit(zxc = getchar()))
        if (zxc == '-')
            qwe = -1;
    while (isdigit(zxc))
        asd = asd * 10 + zxc - '0', zxc = getchar();
    return asd * qwe;
}

inline bool check(int x, int y) { return (x == s || y == s); }
inline int gf(int x) { return (fa[x] == x ? fa[x] : fa[x] = gf(fa[x])); }

inline bool merge(int x, int y)
{
    int fx = gf(x), fy = gf(y);
    if (fx == fy)
        return 0;
    fa[fx] = fy;
    return 1;
}

inline int kruskal()
{
    int num = 0;
    top = 0;
    iota(fa + 1, fa + n + 1, 1);
    for (int i = 1, l = 1, r = 1; i <= m; i++)
        if (l <= graBlack.cnt && graBlack[l].val < graWhite[r].val)
            merge(graBlack[l].to, graBlack[l].nxt), l++;
        else
            num += merge(graWhite[r].to, graWhite[r].nxt), r++;
    return num;
}

inline void checkSol()
{
    int cnt = 0, sum = 0;
    for (int i = 1; i <= graBlack.cnt; i++)
        cnt += merge(graBlack[i].to, graBlack[i].nxt);
    for (int i = 1; i <= graWhite.cnt; i++)
        if (merge(graWhite[i].to, graWhite[i].nxt))
            cnt++, sum++;
    if (cnt != n - 1 || sum > k || graWhite.cnt < k)
        puts("Impossible"), exit(0);
}

inline void makeSol(int mid)
{
    graWhite.init(mid), top = 0;
    iota(fa + 1, fa + n + 1, 1);
    int sum = 0, l = 1, r = 1, i, j;
    for (i = 1; i <= m; i++)
        if (l <= graBlack.cnt && graBlack[l].val <= graWhite[r].val)
            merge(graBlack[l].to, graBlack[l].nxt), l++;
        else
        {
            if (merge(graWhite[r].to, graWhite[r].nxt))
                vis[r] = 1, k--;
            r++;
        };
    l--, r--, top = 0;
    iota(fa + 1, fa + n + 1, 1);
    for (i = 1, j = 1; i <= graBlack.cnt; i++)
    {
        while (j <= r && graWhite[j].val <= graBlack[i].val)
        {
            int ls = j, rs = j;
            int x = graWhite[j].val;
            while (rs < r && graWhite[rs + 1].val == x)
                rs++;
            for (int j = ls; j <= rs; j++)
                if (vis[j] && merge(graWhite[j].to, graWhite[j].nxt))
                    sum += graWhite[j].val, ans[++top] = graWhite[j].id;
            for (int j = ls; j <= rs && k; j++)
                if (merge(graWhite[j].to, graWhite[j].nxt))
                    sum += graWhite[j].val, k--, ans[++top] = graWhite[j].id;
            j = rs + 1;
        }
        if (merge(graBlack[i].to, graBlack[i].nxt))
            sum += graBlack[i].val, ans[++top] = graBlack[i].id;
    }
    while (j <= r)
    {
        int ls = j, rs = j;
        int x = graWhite[j].val;
        while (rs < r && graWhite[rs + 1].val == x)
            rs++;
        for (int j = ls; j <= rs; j++)
            if (vis[j] && merge(graWhite[j].to, graWhite[j].nxt))
                sum += graWhite[j].val, ans[++top] = graWhite[j].id;
        for (int j = ls; j <= rs && k; j++)
            if (merge(graWhite[j].to, graWhite[j].nxt))
                sum += graWhite[j].val, k--, ans[++top] = graWhite[j].id;
        j = rs + 1;
    }
    cout << sum - kl * mid << endl;
}

inline void wqsDichotomous()
{
    int l = -1e9, r = 1e9;
    graBlack.mySort(), graWhite.mySort();
    while (l <= r)
    {
        int mid = (l + r) >> 1;
        graWhite.init(mid);
        int x = kruskal();
        graWhite.init(-mid);
        if (x > k)
            l = mid + 1;
        if (x < k)
            r = mid - 1;
        if (x == k)
            break;
    }
    makeSol((l + r) >> 1);
}

signed main()
{
    n = read(), m = read(), s = read(), kl = k = read();
    iota(fa + 1, fa + n + 1, 1);
    for (int i = 1; i <= m; i++)
    {
        int x = read(), y = read(), z = read();
        if (check(x, y))
            graWhite.add(x, y, z, i);
        else
            graBlack.add(x, y, z, i);
    }
    checkSol(), wqsDichotomous();
    return 0;
}

作者:JiaY19

出处:https://www.cnblogs.com/JiaY19/p/16852549.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   JiaY19  阅读(55)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示