[CSP-S 2019 江西] 网格图

算法

暴力

建图直接跑 Kruskal, 显然能通过 \(64pts\) 的点

正解

分析 Kruskal 的复杂度
发现比较边权非常的浪费, 很显然是不必要的
并查集求环路也浪费了网格图的性质

考虑优化
把每一条边看做一个整体, 整体比较只需要 \(O((n + m) \log (n + m))\)
问题是这样比较之后正确性如何

MST 中一定包含边权最小的边

所以正确性没问题, 但是还需要高效判环

pAG2VQU.png

对于其中

这条环上不属于 \(F\) 的另一条边 \(f\) (一定只有 \(1\) 条)

的证明:
假设不存在 \(f\), 那么 \(T + e \subset F\), 即 \(F\) 中存在一个环, 假设不成立

所以我们知道, 只要你的边权小, 不会构成环, 你就在最小生成树中

所以一直选择横行和纵行中边权最小的边加入最小生成树, 判环的方法是

前提条件 :
[(连完这次之后)目前已经连过的列数] \(\geq 2\) \(\And\) [(连完这次之后)目前已经连过的行数] \(\geq 2\) (即成环的条件)

  • 当我们在连的是行的权值时:如果我们看到连完之后出现了环,那么就可以省去 ([目前已经连过的列数] - \(1\)) 条边

  • 当我们在连的是列的权值时:如果我们看到连完之后出现了环,那么就可以省去 ([目前已经连过的行数] - \(1\))条边

代码

#include <bits/stdc++.h>
const int MAXN = 6e5 + 5;
struct node
{
    int type, val;
} a[MAXN];

bool cmp(node a, node b) { return a.val < b.val; }

int main()
{
    int n, m, x = 0, y = 0;
    long long sum = 0;
    scanf("%d %d", &n, &m);

    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i].val), a[i].type = 1;
    for (int i = n + 1; i <= n + m; i++)
        scanf("%d", &a[i].val), a[i].type = 2;

    std::sort(a + 1, a + n + m + 1, cmp);

    for (int i = 1; i <= n + m; i++)
    {
        if (a[i].type == 1)
        {
            sum += 1ll * (m - 1) * a[i].val;
            x++;
            if (x > 1 && y > 1)
                sum -= 1ll * (y - 1) * a[i].val;
        }
        else
        {
            sum += 1ll * (n - 1) * a[i].val;
            y++;
            if (x > 1 && y > 1)
                sum -= 1ll * (x - 1) * a[i].val;
        }
    }

    printf("%lld", Sum);
    return 0;
}

然而 ctj, 但是也不难写...

总结

善于从暴力中总结出正解

posted @ 2024-10-08 21:12  Yorg  阅读(7)  评论(0编辑  收藏  举报