并查集是一种用于分离集合操作的抽象数据类型,用于处理集合之间的关系,即动态地维护和处理集合元素之间复杂的关系。
例如:初始时n个元素分属不同的n个集合,通过不断的给出元素之间的联系,要求实时的统计元素间的关系(是否存在直接或间接的联系)。这时就有了并查集的用武之地了。元素间是否有联系只要判断两个元素是否属于同一个集合;而给出的元素间的联系,建立这种联系,则只需要合并两个元素各自所属的集合。这些操作都是并查集提供的。
初始化
for(i=1;i<=n;i++)
father[i]=i;
寻找根节点编号&&压缩路径
int find(int x)
{
if(father[x]!=x)
father[x]=find(father[x]);
return father[x];
}
合并两个集合
void unionn(int x,int y)
{
x=find(x);
y=find(y);
father[y]=x;
}
判断元素是否属于同一集合
bool judge(int x,int y)
{
x=find(x);
y=find(y);
if(x==y) return true;
else return false;
}
【题目描述】
或许你并不知道,你的某个朋友是你的亲戚。他可能是你的曾祖父的外公的女婿的外甥女的表姐的孙子。如果能得到完整的家谱,判断两个人是否是亲戚应该是可行的,但如果两个人的最近公共祖先与他们相隔好几代,使得家谱十分庞大,那么检验亲戚关系实非人力所能及。在这种情况下,最好的帮手就是计算机。为了将问题简化,你将得到一些亲戚关系的信息,如Marry和Tom是亲戚,Tom和Ben是亲戚,等等。从这些信息中,你可以推出Marry和Ben是亲戚。请写一个程序,对于我们的关于亲戚关系的提问,以最快的速度给出答案。
【输入】
输入由两部分组成。
第一部分以N,M开始。N为问题涉及的人的个数(1≤N≤20000)。这些人的编号为1,2,3,…, N。下面有M行(1≤M≤1000000),每行有两个数ai,biai,bi,表示已知aiai和bibi是亲戚。
第二部分以Q开始。以下Q行有Q个询问(1≤ Q ≤1000000),每行为ci,dici,di,表示询问cici和didi是否为亲戚。
【输出】
对于每个询问ci,dici,di,输出一行:若cici和didi为亲戚,则输出“Yes”,否则输出“No”。
【输入样例】
10 7
2 4
5 7
1 3
8 9
1 2
5 6
2 3
3
3 4
7 10
8 9
【输出样例】
Yes
No
Yes
这个代码我觉得跟书上基本一样了,咱也搞不清楚为啥一直90分,只能先贴一个90分的代码上来了
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 2e4+5;
int fa[maxn];
int find(int x)
{
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void unite(int x, int y)
{
int x1 = find(x);
int y1 = find(y);
if (x1 != y1)fa[x1] = y1;
}
int main()
{
int n, m, x, y;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)fa[i] = i;
while (m--)
{
scanf("%d%d", &x, &y);
unite(x, y);
}
scanf("%d", &m);
while (m--)
{
scanf("%d%d", &x, &y);
if (find(x) == find(y))printf("Yes\n");
else printf("No\n");
}
}
【题目描述】
或许你并不知道,你的某个朋友是你的亲戚。他可能是你的曾祖父的外公的女婿的外甥女的表姐的孙子。如果能得到完整的家谱,判断两个人是否是亲戚应该是可行的,但如果两个人的最近公共祖先与他们相隔好几代,使得家谱十分庞大,那么检验亲戚关系实非人力所能及。在这种情况下,最好的帮手就是计算机。为了将问题简化,你将得到一些亲戚关系的信息,如Marry和Tom是亲戚,Tom和Ben是亲戚,等等。从这些信息中,你可以推出Marry和Ben是亲戚。请写一个程序,对于我们的关于亲戚关系的提问,以最快的速度给出答案。
【输入】
输入由两部分组成。
第一部分以N,M开始。N为问题涉及的人的个数(1≤N≤20000)。这些人的编号为1,2,3,…, N。下面有M行(1≤M≤1000000),每行有两个数ai,biai,bi,表示已知aiai和bibi是亲戚。
第二部分以Q开始。以下Q行有Q个询问(1≤ Q ≤1000000),每行为ci,dici,di,表示询问cici和didi是否为亲戚。
【输出】
对于每个询问ci,dici,di,输出一行:若cici和didi为亲戚,则输出“Yes”,否则输出“No”。
【输入样例】
10 7
2 4
5 7
1 3
8 9
1 2
5 6
2 3
3
3 4
7 10
8 9
【输出样例】
Yes
No
Yes
#include<bits/stdc++.h>
using namespace std
struct node
{
int x,y;
}f[350][350];
node findth(node k)
{
if((f[k.x][k.y].x==k.x)&&(f[k.x][k.y].y==k.y)){
return k;
}
return f[k.x][k.y]=findth(f[k.x][k.y]);
}
int main()
{
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
f[i][j].x=i;
f[i][j].y=j;
}
}
node k1,k2;
for(int i=1;i<=m;i++){
int x;
int y;
char c;
scanf("%d %d %c\n",&x,&y,&c);
if(c=='D'){
k1=findth(f[x][y]);
k2=findth(f[x+1][y]);
}
if(c=='R'){
k1=findth(f[x][y]);
k2=findth(f[x][y+1]);
}
if((k1.x==k2.x)&&(k1.y==k2.y))
{
printf("%d\n",i);
return 0;
}
else f[k2.x][k2.y]=k1;
}
printf("draw\n");
return 0;
}
1390:食物链【NOI2001】
提交数: 1768 通过数: 887
【题目描述】
动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是"1 X Y",表示X和Y是同类。
第二种说法是"2 X Y",表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1)当前的话与前面的某些真的话冲突,就是假话;
2)当前的话中X或Y比N大,就是假话;
3)当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1≤ N ≤50,000)和K句话(0≤K≤100,000),输出假话的总数。
【输入】
第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。
【输出】
只有一个整数,表示假话的数目。
【输入样例】
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
【输出样例】
3
【提示】
#include<iostream>
#include<cstdio>
using namespace std;
const int N = 1e6;
int p[N];
int findth(int x)
{
if (x == p[x]) return p[x] = x;
return p[x] = findth(p[x]);
}
void unionn(int x, int y)
{
int xx = findth(x);
int yy = findth(y);
if (xx != yy) p[yy] = xx;
}
bool f(int x, int y)
{
return findth(x) == findth(y);
}
int main()
{
int n, k;
scanf("%d %d", &n, &k);
for (int i = 1;i <= 3 * n;i++) p[i] = i;
int flag = 0;
for (int i = 0;i < k;i++) {
int b, x, y;
scanf("%d %d %d", &b, &x, &y);
if (x > n || y > n) flag++;
else {
if (b == 1) {
if (f(x, y + n) || f(x, y + n * 2)) flag++;
else {
unionn(x, y);//同一类型放在一个集合
unionn(x + n, y + n);
unionn(x + 2 * n, y + 2 * n);
}
}
else if (b == 2) {
if (f(x, y) || f(x, y + 2 * n)) flag++;
else {
unionn(x, y + n);//这里是把x吃y换成y吃x的操作
unionn(x + n, y + 2 * n);
unionn(x + 2 * n, y);
}
}
}
}
printf("%d\n", flag);
return 0;
}