图论&数据结构——并查集

Wikioi 4246 NOIP模拟赛Day2T1 奶牛的身高 

题目描述 Description

奶牛们在FJ的养育下茁壮成长。这天,FJ给了奶牛Bessie一个任务,去看看每个奶牛场中若干只奶牛的身高,由于Bessie是只奶牛,无法直接看出第i只奶牛的身高,而只能看出第i只奶牛与第j只奶牛的身高差,其中第i 只奶牛与第j只奶牛的身高差为A(i<=n)。当A大于0时表示这只奶牛比前一只奶牛高A cm,小于0时则是低。现在,FJ让Bessie总共去看了m次身高,当然也就传回给FJ m对奶牛的身高差,但是Bessie毕竟是奶牛,有时候眼睛可能会不好使……(大雾)你的任务是帮助FJ来判断是不是需要给Bessie看看眼睛了……

注:Hj-Hi=A 注意T1的样例 注意注意注意 重要的事情说三遍。

输入描述 Input Description

第一行为一个正整数w,表示有w组数据,即w个奶牛场,需要你判断。每组数据的第一行为两个正整数n和m,分别表示对应的奶牛场中的奶牛只数以及看了多少个对奶牛身高差。接下来的m行表示Bessie看m次后传回给FJ的m条信息,每条信息占一行,有三个整数s,t和v,表示第s只奶牛与第t只奶牛的身高差为v。

输出描述 Output Description

包含w行,每行是”Bessie’s eyes are good”或”Bessie is blind.”(不含双引号),其中第i行为”Bessie’s eyes are good”当且仅当第i组数据,即无法从第i个奶牛场传回的身高差判断Bessie视力好不好;第i行为”Bessie is blind.”当且仅当第i组数据,即从第i个奶牛场传回的身高差是有问题的。

样例输入 Sample Input

2

3 3

1 3 10

2 3 5

1 2 5

4 3

1 4 100

3 4 50

1 3 100

样例输出 Sample Output

Bessie’s eyes are good

Bessie is blind.

数据范围及提示 Data Size & Hint

对于30%的数据,保证n<=100,m<=1000;

对于100%的数据,保证w<=100,n<=1000,m<=30000,|A|<=30000.

思路:

可以使用一个并查集,维护父亲节点减去儿子节点的值(儿子节点减去父亲节点也可,但需要把之后的维护反过来)用rank表示,由于a-b+b-c=a-c的性质,路径压缩的时候只要累加到根的权值和就是该点对于根的权值。合并的时候则要计算一下,如果要合并的两个节点是a,b,他们的父亲节点是x,y,如果x=y,那么说明x-a=rank[a],y-b=rank[b],a-b=v(v是输入的值),由此可得a-b=rank[y]-rank[x],判断是否矛盾即可,如果a,b不同根,那么一定没有矛盾,只需要合并即可,但是合并的时候要重新处理a,b的rank值,还是像刚才那样设元,那么就可以得出rank[y]=rank[a]-rank[b]+d,问题到此就解决了

代码:

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<string.h>
 4 #include<stdlib.h>
 5 #include<algorithm>
 6 #include<string>
 7 #include<math.h>
 8 using namespace std;
 9
10 int f[50005]={0};
11 int rank[50005]={0};
12 int n;
13 void initial()
14 {
15     for(int i=1;i<=n;i++)
16     {
17         f[i]=i; rank[i]=0;
18     }
19 }
20 int find(int x){
21     if(x==f[x]) return x;
22     int fa=f[x];
23     f[x] = find(f[x]);
24     rank[x] += rank[fa];
25     return f[x];
26 }
27 bool istrue(int x, int y, int d){
28     int ra=find(x), rb=find(y);
29     if(ra==rb){
30         if(rank[y]-rank[x]!=d) return false;
31         return true;
32     }
33
34     f[rb] = ra;
35
36     rank[rb] = rank[x]-rank[y]+d;
37
38     return true;
39
40 }
41
42
43
44 int main()
45
46 {
47
48     int iw;
49
50     scanf("%d",&iw);
51
52     while (iw--)
53
54     {
55
56      int k,i,x,y,d; int ans=0;
57
58     scanf("%d%d",&n,&k);
59
60     initial();
61
62     bool flag=true;
63
64     for(i=1;i<=k;i++)
65
66     {
67
68         scanf("%d%d%d",&x,&y,&d);
69
70         if( !istrue(x,y,d) )
71
72           {
73
74             if (flag) printf("Bessie is blind.\n");
75
76             flag=false;
77
78           }
79
80         }
81
82     if (flag)
83
84        printf("Bessie‘s eyes are good\n");
85
86     }
87
88 }  

 NOIP模拟赛 集合

【题目描述】

现在给你一些连续的整数,它们是从 A 到 B 的整数。一开始每个整数都属于各自的集 合,然后你需要进行一下的操作: 每次选择两个属于不同集合的整数,如果这两个整数拥有大于等于 P 的公共质因数, 那么把它们所在的集合合并。 反复如上操作,直到没有可以合并的集合为止。 现在 Caima 想知道,最后有多少个集合。

【输入格式】

一行,三个整数 A,B,P。

【输出格式】

一个数,表示最终集合的个数。

【数据规模】

A≤B≤100000; 2≤P≤B。

【输入样例】

10 20 3

【输出样例】

7

【注意事项】

有 80%的数据 B≤1000。 样例解释{10,20,12,15,18},{13},{14},{16},{17},{19}。

思路:

逐一枚举质因数,然后并查集合并

代码:

 1 #include<cstring>
 2 #include<iostream>
 3 using namespace std;
 4 #define N 100005
 5
 6 bool vis[N];
 7 int p[N], cnt, phi[N],father[N],prime[N],choose[N];
 8 int a,b,k;
 9 int Eratosthenes (int n){
10         int i, j, k;
11         phi[1] = 1;
12         for (i = 2; i < n; ++i){
13                 if (!vis[i]) p[cnt++] = i,prime[i] = 1;
14                 for (j = i; j < n; j += i) {
15                         if (!phi[j]) phi[j] = j;
16                         phi[j] = phi[j] / i * (i - 1);
17                         vis[j] = true;
18                 }
19         }
20         return cnt;
21 }
22 int findf(int x){
23      int j = x,t;
24      while(father[x] != x) x = father[x];
25      while(j != x){
26         t = father[j];
27         father[j] = t;
28         j = t;
29      }
30      return x;
31 }
32 int main(){
33     //freopen("set.in","r",stdin);
34     //freopen("set.out","w",stdout);
35     cin>>a>>b>>k;
36     for(int i = 1;i <= b;i++) father[i] = i;
37     Eratosthenes(b);
38     for(int i = k;i <= b;i++){
39             if(prime[i]){
40               for(int j = 1;j * i <= b;j++){
41                       if(j*i < a) continue;
42                       father[findf(j*i)] = findf(i);
43               }
44             }
45     }
46     int ans = 0;
47
48     for(int i = 1;i <= b;i++) father[i] = findf(i);
49     for(int i = a;i <= b;i++){
50             if(!choose[father[i]]){
51               choose[father[i]] = 1;
52               ans++;
53             }
54     }
55     cout<<ans;
56     return 0;
57 }

时间: 2024-08-05 12:57:22

图论&数据结构——并查集的相关文章

POJ 1984 Navigation Nightmare (数据结构-并查集)

Navigation Nightmare Time Limit: 2000MS   Memory Limit: 30000K Total Submissions: 4072   Accepted: 1615 Case Time Limit: 1000MS Description Farmer John's pastoral neighborhood has N farms (2 <= N <= 40,000), usually numbered/labeled 1..N. A series o

POJ 1703 Find them, Catch them(数据结构-并查集)

Find them, Catch them Description The police office in Tadu City decides to say ends to the chaos, as launch actions to root up the TWO gangs in the city, Gang Dragon and Gang Snake. However, the police first needs to identify which gang a criminal b

读书笔记 之 数据结构(并查集详解)(POJ1703)

<ACM/ICPC算法训练教程>读书笔记-这一次补上并查集的部分.将对并查集的思想进行详细阐述,并附上本人AC掉POJ1703的Code. 在一些有N个元素的集合应用问题中,通常会将每个元素构成单元素集合,然后按照一定顺序将同属一组的集合合并,期间要反复查找每一个元素在哪个集合中.这类问题往往看似简单,但是数据量很大,因此容易造成TLE或MLE,也就是空间度和时间度极其复杂.因此在这里,我们引入一种抽象的特殊数据结构——并查集. 并查集:类似一个族谱,每个结点均有一个father[x]来表示x

HDU 1988 Cube Stacking (数据结构-并查集)

Cube Stacking Time Limit: 2000MS   Memory Limit: 30000K Total Submissions: 18900   Accepted: 6568 Case Time Limit: 1000MS Description Farmer John and Betsy are playing a game with N (1 <= N <= 30,000)identical cubes labeled 1 through N. They start w

数据结构---并查集

并查集,顾名思义,合并 查找 集合: 并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题.常常在使用中以森林来表示. 对于概念等等的这里不再赘述,直接讲解应用. [初始化] MakeSet,将每一个节点的父节点置为本身:rank(秩),是节点构成树的深度 void MakeSet(){ for(int i=1;i<=maxn;i++){ parent[i].value = i; parent[i].rank=0; } } [查找] 一直向上查找,直到

实用数据结构---并查集

题目描述 Description 若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系. 规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚.如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚. 输入描述 Input Description 第一行:三个整数n,m,p,(n<=5000,m<=5000,p<=5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系. 以下m行:每行两个数Mi,Mj,

数据结构--并查集的原理及实现

一,并查集的介绍 并查集(Union/Find)从名字可以看出,主要涉及两种基本操作:合并和查找.这说明,初始时并查集中的元素是不相交的,经过一系列的基本操作(Union),最终合并成一个大的集合. 而在某次合并之后,有一种合理的需求:某两个元素是否已经处在同一个集合中了?因此就需要Find操作. 并查集是一种 不相交集合 的数据结构,设有一个动态集合S={s1,s2,s3,.....sn},每个集合通过一个代表来标识,该代表中集合中的某个元素. 比如,若某个元素 x 是否在集合 s1 中(Fi

数据结构——并查集

一.并查集的定义 并查集是一种维护集合的数据结构,它的名字中"并""查""集"分别取自 Union(合并).Find(查找).Set(集合)这 3 个单词.也就是说,并查集支持下面两个操纵: 合并:合并两个集合. 查找:判断两个元素是否在一个集合. 并查集的实现就是用一个数组: int father[N]; // 表示元素的父亲结点 例如 father[1]=2 就表示元素 1 的父亲结点是元素 2.另外,如果 father[i]==i,则说明元

HDU 5652 图论之并查集

点击打开链接 题意:上边是中国,下边是印度,黑点的部分不可以走,下面的矩阵1代表黑点不能走,然后给了Q,每一次将一个点变成黑点,即不能走,问最少多少次就可以完成 思路:如果有一条黑点连成的线,从走到右的说明我们达成了目的,那就好办了,每一个黑点可以与周围的8个黑点相连,这样我们在最左边建个汇点最右边建个源点,每次询问这两个点在不在一个集合就行了,用并查集轻松实现,只要两个点相连就放到一个集合里 #include <stdio.h> #include <string.h> #incl