2021牛客暑期多校训练营3 B Black and white

B Black and white

题目链接:

第三场B题

题目大意:

用题目所给算法给你一个矩阵,将一个点涂黑的花费为该点上的值,如果一个2*2的区域内已经有了三个位置被涂黑,涂黑第四个位置不需要代价,问涂黑整个矩阵的最小代价。

题解:

可以证明涂黑整个矩阵只需要首先涂黑n+m-1个点,当然这n+m-1个点需要达到某种条件,即任意一个点所在的行和列都存在被涂黑的点。
以下几个图符合条件:


下面这个不符合条件,因为存在某个位置的行列都没有涂黑:

如果直接对于矩阵来思考最小值比较困难,不如换个思路:
可以用矩阵来表示两个点连边的代价(如p[i][j]=k表示将点i与点j建立链接需要的代价),如果这样想的话上面几个图就会有另一种解释方式。

这里的意思是从某个边开始,一定可以在同行或者同列找到下一个边
(如果某种涂黑的方案是有效解的话,那么上面的每一个边都可以通过这种方式链接到所有边,换句话说就是构造一个最小代价的联通图,链接矩阵轴上的每一个点,矩阵的轴上一共有n+m个点,而我们需要n+m-1条边,这样就不难想到这题需要最小生成树)
经过上面的思考,这题的基本模型就出来了:
给你n*m条边,找出联通n+m个点的最小权值,嗯~非常经典的最小生成树

由于这题数据量比较大,据说会卡掉使用sort的克鲁斯卡尔算法,这里提供一种不用sort来用克鲁斯卡尔的方法 (你就不会用prim?抱歉,我和他不熟)

多亏了本题的数据输入使用题目给的代码,说实话我还没见过这种出数据的方式,把大量题目数据运算交给选手的电脑跑,算法岂是如此不便之物!,我们可以直接在输入数据的时候就让其按照升序存储。

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


const int N=5e3+5;
const int inf=0x3f3f3f3f;
int a[N*2];
int fid(int k)
{
	if(a[k]==k) return k;
	else return a[k]=fid(a[k]);
}
vector< pair<int ,int > > e[100005];//存储权值对应的边,如e[2]{3,5}表示从点3到点n+5有一条权值为2的边供你选择。
signed main()
{
	int n,m,x,b,c,d,p;
    cin>>n>>m>>x>>b>>c>>d>>p;
    for(int i=1;i<=n;i++)
    	for(int j=1;j<=m;j++)
    	{
			x=(x*x*b+x*c+d)%p;
			e[x].push_back(make_pair(i,n+j));//因为点有n+m个,但是题目给的是i,j,所以对于每个j,使其对应第n+j个点
		}
	for(int i=1;i<=n+m;i++) a[i]=i;//并查集初始化
	int ans=0,cnt=0;
	for(int i=0;i<p;i++)//直接从小到大枚举权值,省去了sort,真是
	{
		for(int j=0;j<e[i].size();j++)
		{
			int x=fid(e[i][j].first),y=fid(e[i][j].second);
			if(x!=y)
			{
				cnt++;
				a[y]=x;
				ans+=i;
			}
		}
		if(cnt==n+m-1) break;
	}
	cout<<ans<<endl;
	return 0;
}

评论 2 您还未登录,请先 登录 后发表或查看评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:深蓝海洋 设计师:CSDN官方博客 返回首页

打赏作者

鬼人明灯

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值