题目背景
本题译自 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)的样品后才能得到。
子任务
注意:当且仅当你通过了一个子任务下的所有测试点,你将获得此子任务的分数。
子任务编号 | 分数 | n | m | q |
---|---|---|---|---|
1 | 10 | n=2 | m=2 | 40≤q≤4 |
2 | 17 | 1≤n≤2 | 1≤m≤20 | 0≤q≤20 |
3 | 8 | 1≤n≤20 | 1≤m≤20 | q=0 |
4 | 20 | 1≤n≤20 | 1≤m≤20 | 0≤q≤400 |
5 | 30 | 1≤n≤1×10^4 | 1≤m≤1×10^4 | 1≤q≤1×10^5 |
6 | 15 | 1≤n≤2×10^5 | 1≤m≤2×10^5 | 1≤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;
}