2021牛客暑期多校训练营3补题

2021牛客暑期多校训练营3:B题(Black and white)【补题】


原题链接:传送门


先理解题意,这题比较特殊的是Goodeat的“技能”,使用技能的场景如下图:
...
对于 A ( i , j ) A(i,j) A(i,j),此时,格子 A ( 1 , 2 ) , A ( 1 , 4 ) , A ( 3 , 4 ) 以 及 A ( 3 , 2 ) A(1,2),A(1,4),A(3,4)以及A(3,2) A(1,2),A(1,4),A(3,4)A(3,2) 即为两行两列的四个相交的正方形,而其中三个已经涂黑: A ( 1 , 2 ) , A ( 1 , 4 ) , A ( 3 , 4 ) A(1,2),A(1,4),A(3,4) A(1,2),A(1,4),A(3,4),则红色箭头标注指向的 A ( 3 , 2 ) A(3,2) A(3,2)就可以不花费代价涂黑。

这里要注意,不花费代价的涂黑第四个格子时,对于其他三个涂黑的格子不论是使用技能还是花费代价涂黑的,只要满足使用技能的条件,就可以。
也就是说若使用技能涂黑某个格子后,这个格子又形成了一个新的四个相交的正方形且有三个涂黑了,那也是可以继续使用技能的。

回到题目本身,我们的目的是求涂黑整个棋盘的最小代价

思考这个技能如何使用最优 ,当然了,我当场并没有想出来
可以证明出,对于这个权重矩阵,只要有 n + m − 1 n + m - 1 n+m1 个点涂黑了,并且满足条件:对于任意一行或任意一列都至少存在一个涂黑的点,那剩余的点都可以使用技能全部涂黑。
证明参考这篇大佬博客:传送门

此时,这个题目的权重矩阵 A ( i , j ) A(i,j) A(i,j)可以考虑表示为边,即有 n + m n + m n+m 个点, n ∗ m n * m nm 条边。

至此,已经不难想到这题需要最小生成树,将权重矩阵横轴的 1~m 视为 点1~m ,竖轴的 1~n 视为点m+1~n+m,不难发现,若使用最小生成树使其联通,一定满足最低限度使用技能的条件,这个画个图就明白了,于是这题上Prim或者Kruskal就搞定了。
这是以示例一表现出来的图 1:

图 2:
在这里插入图片描述

注1:有些Prim的模板中,对于某个点是否已经选择的处理方式不同,有些使用的是将lowcost[i]标注为0并以此判断已经选择过。这种方式在这题不可行,因为可能会出现某条边权为0的情况,这样显然是错误的。所以对于本题应该用布尔数组(true/false)对某个点的是否选择进行处理。
注2:查阅其他人的题解时发现,本题如果用Kruskal似乎需要桶排,不然会超时,我是用Prim做的,这一点大家自行验证吧。

AC代码:

#include <bits/stdc++.h>

using namespace std;
using ll = long long;
const int maxn = 1e4 + 10;
const int inf = 0x3f3f3f;

int graph[maxn][maxn];
int lowcost[maxn];
bool st[maxn];

int Prim(int n)
{
	//初始化
	int ans = 0;
	memset(st, false, sizeof(st));
	memset(lowcost, inf, sizeof(lowcost));
	st[1] = true;
	for (int i = 2; i <= n; i++) lowcost[i] = graph[1][i];


	for (int i = 2; i <= n; i++) {
		int minc = inf;
		int p = -1;

		//选择目前可选边中的最小权值的边,并记录连接的点
		for (int j = 2; j <= n; j++) {
			if (!st[j] && minc > lowcost[j]) {
				minc = lowcost[j];
				p = j;
			}
		}

		ans += minc;
		st[p] = true;//标记选中的点

		//因为点集加入了新的点,所以需要对最小边权更新
		for (int j = 2; j <= n; j++) {
			if (!st[j] && lowcost[j] > graph[p][j])
				lowcost[j] = graph[p][j];
		}
	}

	return ans;
}

int main()
{
	int n, m, a, b, c, d, p; cin >> n >> m >> a >> b >> c >> d >> p;
	memset(graph, inf, sizeof graph);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++) {
			a = ((ll)a * a * b + (ll)a * c + d) % p;
			graph[i + m][j] = graph[j][i + m] = a;
		}

	cout << Prim(n + m);
	return 0;
}

本篇文章仅代表个人观点,如有不同意见欢迎交流。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TIT_0

喵喵喵?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值