P3623 [APIO2008]免费道路 题解

最小生成树好题。

下面规定鹅卵石路长度为 1,水泥路长度为 0,则题中要求的是一棵边权为 \(k\) 的生成树。

这道题一开始的时候我会认为边权为 1 的边可以随便加,毕竟根据样例来看,删这一条换另一条并没有什么问题。

于是就有一种做法是先选好 \(k\) 条 1 边,然后剩下的 0 边随意加。

然而根据下面这张图,你就会发现上述做法有问题:

在这里插入图片描述

如果此时 \(k=1\),而你又挑选了 \((1,3)\) 的话……

此时图根本不连通!

因此,有一些边是一定要加的。

那么怎么知道哪些边一定要加呢?

考虑对图做一遍最小生成树,记录一下加入到最小生成树的 1 边数量,这些 1 边就是一定要加的,因为如果删了这些边就不合法了。

然后呢?先加入这些必须加的 1 边,然后在剩下的所有 1 边中加边直到加满,这些边是可以随便选的。最后拿 0 边补齐即可。

无解情况:

  • 必须加的 1 边大于 \(k\)
  • 总共的 1 边不足 \(k\)

代码:

/*
========= Plozia =========
    Author:Plozia
    Problem:P3623 [APIO2008]免费道路
    Date:2021/4/22
========= Plozia =========
*/

#include <bits/stdc++.h>

typedef long long LL;
const int MAXN = 20000 + 10, MAXM = 100000 + 10;
int n, m, k;
struct node { int x, y, z; } a[MAXM];
struct Union
{
    int fa[MAXN];
    void init() { for (int i = 1; i <= n; ++i) fa[i] = i; }
    int gf(int x) { return (fa[x] == x) ? x : fa[x] = gf(fa[x]); }
    void hb(int x, int y) { if (gf(x) != gf(y)) fa[fa[x]] = fa[y]; }
}u;

int read()
{
    int sum = 0, fh = 1; char ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = (sum << 3) + (sum << 1) + (ch ^ 48);
    return sum * fh;
}
bool cmp1(const node &fir, const node &sec) { return fir.z < sec.z; }
bool cmp2(const node &fir, const node &sec) { return fir.z > sec.z; }

int main()
{
    n = read(), m = read(), k = read();
    for (int i = 1; i <= m; ++i) a[i].x = read(), a[i].y = read(), a[i].z = read() ^ 1;
    std::sort(a + 1, a + m + 1, cmp1);
    u.init(); int tmp, sum;
    tmp = 0, sum = n;
    for (int i = 1; i <= m; ++i)
    {
        if (u.gf(a[i].x) != u.gf(a[i].y))
        {
            u.hb(a[i].x, a[i].y);
            if (a[i].z == 1) a[i].z = -1, ++tmp;
            --sum; if (sum == 1) break ;
        }
    }
    if (tmp > k) { printf("no solution\n"); return 0; }
    if (sum != 1) { printf ("no solution\n"); return 0; }
    std::sort(a + 1, a + m + 1, cmp2);
    u.init(); sum = n;
    for (int i = 1; i <= m; ++i)
        if (a[i].z == -1) { --sum; u.hb(a[i].x, a[i].y); }
    for (int i = 1; i <= m; ++i)
    {
        if (sum == 1) break ;
        if (a[i].z == -1) continue ;
        if (tmp == k && a[i].z == 1) continue ;
        if (u.gf(a[i].x) != u.gf(a[i].y))
        {
            u.hb(a[i].x, a[i].y);
            if (a[i].z == 1) ++tmp, a[i].z = -1;
            if (a[i].z == 0) a[i].z = -2;
            --sum; if (sum == 1) break ;
        }
    }
    if (tmp != k) { printf("no solution\n"); return 0; }
    for (int i = 1; i <= m; ++i)
    {
        if (a[i].z == -2) printf("%d %d 1\n", a[i].x, a[i].y);
        if (a[i].z == -1) printf("%d %d 0\n", a[i].x, a[i].y);
    }
    return 0;
}
posted @ 2022-04-17 15:20  Plozia  阅读(19)  评论(0编辑  收藏  举报