P5089 [eJOI2018]元素周期表

该博客讨论了如何解决eJOI2018竞赛中的一个关于元素周期表的问题。给定一个矩阵,表示不同元素及其组合可以生成新元素的情况,目标是找出至少需要购买多少个元素样品来收集所有元素。博主提出了一个利用并查集解决这个问题的策略,通过分析元素之间的关系,得出购买元素的最少数量与连通块的数量有关。代码实现部分展示了如何运用这个策略来计算答案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目背景

本题译自 eJOI2018 Problem D「Chemical table」

题目描述

Innopolis 大学的教授正努力研究元素周期表。他们知道,有 n \times mn×m 种元素,形成了一个 nn 行 mm 列的矩阵。

研究表明,如果元素周期表上有一个元素 A,且元素 B 与它在同一列(A 与 B 不能在同一周期),元素 C 在同一周期(A 与 C 不能在同一列),那么,科学家就可以用这三种元素通过核聚变合成第四种元素 D 的样品,D 与 B 在同一周期,与 C 在同一列。
简而言之,如果有在元素周期表中位置为 (r_1, c_1),\ (r_1, c_2),\ (r_2, c_1)(r1​,c1​), (r1​,c2​), (r2​,c1​) (其中 r_1 \neq r_2, c_1 \neq c_2r1​​=r2​,c1​​=c2​)的三种元素的样品,就可以生成位置为 (r_2, c_2)(r2​,c2​) 的样品。如图所示:

注意:在核聚变中被使用的样品并不会消失,它们可以参与之后的反应;反应得到的样品也可以参与反应。

他们已经获得了 qq 种元素的样品。为了集齐所有元素的样品,他们会购买一些样品,然后利用核聚变制造出剩下元素的样品。
请求出他们至少需要购买的元素样品的数量。

输入格式

第一行, 33 个整数 n, m, q \ (1 \le n, m \le 2 \times 10^5, 0 \le q \le \min \{n \times m, 2 \times 10^5\})n,m,q (1≤n,m≤2×105,0≤q≤min{n×m,2×105}) 。
之后的 qq 行,每行 22 个整数 r_i, c_i \ (1 \le r_i \le n, 1 \le c_i \le m)ri​,ci​ (1≤ri​≤n,1≤ci​≤m) 。保证给定的元素互不相同。

输出格式

输出一个整数,表示至少需要购买的元素样品的数量。

输入输出样例

输入 #1复制

2 2 3
1 2
2 2
2 1

输出 #1复制

0

输入 #2复制

1 5 3
1 3
1 1
1 5

输出 #2复制

2

输入 #3复制

4 3 6
1 2
1 3
2 2
2 3
3 1
3 3

输出 #3复制

1

说明/提示

样例解释

说明

每个样例解释中有两个矩阵。
第一个表示初始状况(其中,打叉的是原本就有样品的元素)。
第二个表示最终集齐样品时的状况(其中,蓝圈代表核聚变得到的样品,蓝圈中的数字表示得到样品的顺序,红圈表示购买的样品)。

样例解释 1

通过给定的三种元素,可以得到第四种元素的样品。

 

样例解释 2

由于给定的元素只有一行,无法使用核聚变,只能购买剩余的两种元素的样品。

 

样例解释 3

集齐所有元素的方法不唯一,以下是一种方法。其中,元素 (4, 2)(4,2) 只有在购买元素 (4, 1)(4,1) 的样品,和反应得到元素 (1, 1)(1,1)的样品后才能得到。

 


子任务

注意:当且仅当你通过了一个子任务下的所有测试点,你将获得此子任务的分数。

子任务编号分数nmq
110n=2m=240≤q≤4
2171≤n≤21≤m≤200≤q≤20
381≤n≤201≤m≤20q=0
4201≤n≤201≤m≤200≤q≤400
5301≤n≤1×10^41≤m≤1×10^41≤q≤1×10^5
6151≤n≤2×10^51≤m≤2×10^51≤q≤2×10^5

Solution

发现对于同一列存在两行为1,另一列中其中同一行为1,则另一行为1。

推广:同一列中的所有1的行,若另一列的这些行中存在一个1,则其他行都为1。

那么条件是这两列都有1,并且这些行都有1,那么可以将所有的1行和列拆开来,看成是二分图,每个1的行向列连一条边。

发现每次添加点的操作,是在已经连通的三个点中把没连的边给连上,连通块的数量没有改变。

从而可以推出:任何一个连通块中剩下的边都是可以直接加上的。

那么现在需要把矩阵中所有点都填满,相当于所有的边都要连上,那么根据上面的结论,只需要加入最少的边使所有的点构成一个连通块即可,自然答案是连通块数量减1。

这题最有价值的地方就是注意数组要开两倍 的N

 

Code 

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define I int
#define F(i,a,b)  for(I i=a;i<=b;i++)
#define N 200004
using namespace std;
I n,m,q,f[N<<1],ans,x,y;
void R(I &x){
	x=0;I w=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') w=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	x*=w;
}
I get(I x){return f[x]==x?x:f[x]=get(f[x]);}
I main(){
	freopen("elements.in","r",stdin);
	freopen("elements.out","w",stdout);
	R(n),R(m),R(q);
	F(i,1,n+m) f[i]=i; 
	F(i,1,q){
		R(x),R(y);
		x=get(x),y=get(y+n);
		if(x!=y) f[x]=y;
	}
	F(i,1,n+m) ans+=(get(i)==i);
	printf("%d\n",ans-1);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值