算法
暴力
建图直接跑 Kruskal, 显然能通过 \(64pts\) 的点
正解
分析 Kruskal 的复杂度
发现比较边权非常的浪费, 很显然是不必要的
并查集求环路也浪费了网格图的性质
考虑优化
把每一条边看做一个整体, 整体比较只需要 \(O((n + m) \log (n + m))\)
问题是这样比较之后正确性如何
MST 中一定包含边权最小的边
所以正确性没问题, 但是还需要高效判环
对于其中
这条环上不属于 \(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 = x < n ? x + 1 : x;
if (x > 1 && y > 1)
sum -= 1ll * (y - 1) * a[i].val;
}
else
{
sum += 1ll * (n - 1) * a[i].val;
y = y < m ? y + 1 : y;
if (x > 1 && y > 1)
sum -= 1ll * (x - 1) * a[i].val;
}
}
printf("%lld", Sum);
return 0;
}
然而 ctj, 但是也不难写...
总结
善于从暴力中总结出正解
标签:node,val,int,sum,网格,1ll,2019,CSP,连过 From: https://www.cnblogs.com/YzaCsp/p/18452410