CF1013D Chemical table
题目链接:http://codeforces.com/contest/1013/problem/D
题目大意:
给出一个 \(n \times m\) 的表格,表格上有一些初始点。若有这样的三个点:\((r_1, c_1), (r_1, c_2), (r_2, c_1)\),则由这三个点能生成出点 \((r_2, c_2)\)。现问在初始点的基础上最少添加多少个点,能使得初始点和添加的点及它们生成出来的点能填满整个表格。
知识点: 并查集
解题思路:
可以将每一个点看成是将所在的行和列联系起来。如此一来,对于 \((r_1, c_1) + (r_1, c_2) + (r_2, c_1) \Rightarrow (r_2, c_2)\),我们可以理解为:因为 \(r_1\) 和 \(c_1\),\(r_1\) 和 \(c_2\),\(r_2\) 和 \(c_1\) 都相应地联系起来了,那么 \(r_2\) 和 \(c_2\) 也联系起来了。所以我们只要把所有的行和列都联系起来,生成出来的点就能填满整个表格。那么问题就转换成问最少建立多少联结,能将所有的行和列都联系起来。可以用并查集解决。
AC代码
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 #define lson l,m,rt<<1 5 #define rson m+1,r,rt<<1|1 6 const int MAXN=200000+5; 7 const int INF=0x3f3f3f3f; 8 const int MOD=1e9+7; 9 10 int fa[MAXN<<1]; 11 int finds(int x){ 12 if(fa[x]==x) return x; 13 return fa[x]=finds(fa[x]); 14 } 15 int main(){ 16 int r,c,n,m,q; 17 scanf("%d%d%d",&n,&m,&q); 18 for(int i=1;i<=n+m;i++) fa[i]=i; 19 for(int i=0;i<q;i++){ 20 scanf("%d%d",&r,&c); 21 int f1=finds(r),f2=finds(c+n); 22 if(f1!=f2) fa[f1]=f2; 23 } 24 int ans=0; 25 int root=finds(1); 26 for(int i=1;i<=n+m;i++){ 27 int temp=finds(i); 28 if(temp!=root){ 29 ans++; 30 fa[temp]=root; 31 } 32 } 33 printf("%d\n",ans); 34 return 0; 35 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”