并查集

1.​​A - How Many Tables​

Today is Ignatius' birthday. He invites a lot of friends. Now it's dinner time. Ignatius wants to know how many tables he needs at least. You have to notice that not all the friends know each other, and all the friends do not want to stay with strangers. 

One important rule for this problem is that if I tell you A knows B, and B knows C, that means A, B, C know each other, so they can stay in one table. 

For example: If I tell you A knows B, B knows C, and D knows E, so A, B, C can stay in one table, and D, E have to stay in the other one. So Ignatius needs 2 tables at least. 

Input

The input starts with an integer T(1<=T<=25) which indicate the number of test cases. Then T test cases follow. Each test case starts with two integers N and M(1<=N,M<=1000). N indicates the number of friends, the friends are marked from 1 to N. Then M lines follow. Each line consists of two integers A and B(A!=B), that means friend A and friend B know each other. There will be a blank line between two cases. 
Output

For each test case, just output how many tables Ignatius needs at least. Do NOT print any blanks. 

Sample Input

2
5 3
1 2
2 3
4 5

5 1
2 5

Sample Output

2
4

解题思路:并查集的入门级别,就是有熟悉的人就会做一张桌子上,然计算一共需要摆多少张桌子。这是我们就会用到并查集将相互认识的人并到一块
初始时每个人都是一个单独的个体,当输入认识的人后,就会出现一个总的认识的人,最后只要判断谁还是和初始值一样,总和就为要摆的桌子的数。
代码:
#include<iostream>
using namespace std;
const int N=1010;
int s[N];
int t;

int find_set(int root)
{
int son,temp;
son=root;
while(root!=s[root])
root=s[root];
while(son!=root)
{
temp=s[son];
s[son]=root;
son=temp;
}
return root;
}
void join(int x,int y)
{
x=find_set(x);
y=find_set(y);
if(x!=y)
s[x]=s[y];
}
int main()
{
int i,j,n,m,x,y;
cin>>t;
while(t--)
{
cin>>n>>m;
for(i=1;i<=n;i++)
s[i]=i;
for(i=0;i<m;i++)
{
cin>>x>>y;
join(x,y);
}
int ans=0;
for(i=1;i<=n;i++)
{
if(s[i]==i)
ans++;
}
cout<<ans<<endl;
}
return 0;
}

2.​​B - Ubiquitous Religions​

There are so many different religions in the world today that it is difficult to keep track of them all. You are interested in finding out how many different religions students in your university believe in. 

You know that there are n students in your university (0 < n <= 50000). It is infeasible for you to ask every student their religious beliefs. Furthermore, many students are not comfortable expressing their beliefs. One way to avoid these problems is to ask m (0 <= m <= n(n-1)/2) pairs of students and ask them whether they believe in the same religion (e.g. they may know if they both attend the same church). From this data, you may not know what each person believes in, but you can get an idea of the upper bound of how many different religions can be possibly represented on campus. You may assume that each student subscribes to at most one religion.

Input

The input consists of a number of cases. Each case starts with a line specifying the integers n and m. The next m lines each consists of two integers i and j, specifying that students i and j believe in the same religion. The students are numbered 1 to n. The end of input is specified by a line in which n = m = 0.

Output

For each test case, print on a single line the case number (starting with 1) followed by the maximum number of different religions that the students in the university believe in.

Sample Input

10 9
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
10 4
2 3
4 5
4 8
5 8
0 0

Sample Output

Case 1: 1
Case 2: 7
解题思路:宗教信仰,初始每个人都有一个宗教信仰,通过m次询问,有m对同学的宗教信仰相同,求学校里一共有多少宗教信仰。
这也和上面一道题思路差不多。
#include<iostream>
#include<cstdio>
using namespace std;
const int N=50010;
int s[N];
int n,m;
int find_set(int root)
{
int son,temp;
son=root;
while(root!=s[root])
root=s[root];
while(son!=root)
{
temp=s[son];
s[son]=root;
son=temp;
}
return root;
}
void join(int x,int y)
{
x=find_set(x);
y=find_set(y);
if(x!=y)
s[x]=y;
}
int main()
{
int i,j,x1,y1,k=1;
while(scanf("%d%d",&n,&m)&&n&&m)
{
for(i=1;i<=n;i++)
s[i]=i;

for(i=0;i<m;i++)
{
scanf("%d%d",&x1,&y1);
join(x1,y1);
}
int ans=0;
for(i=1;i<=n;i++)
{
if(s[i]==i)
ans++;
}
cout<<"Case "<<k++<<": "<<ans<<endl;
}
return 0;
}

3.​​C - The Suspects​

 

Severe acute respiratory syndrome (SARS), an atypical pneumonia of unknown aetiology, was recognized as a global threat in mid-March 2003. To minimize transmission to others, the best strategy is to separate the suspects from others. 
In the Not-Spreading-Your-Sickness University (NSYSU), there are many student groups. Students in the same group intercommunicate with each other frequently, and a student may join several groups. To prevent the possible transmissions of SARS, the NSYSU collects the member lists of all student groups, and makes the following rule in their standard operation procedure (SOP). 
Once a member in a group is a suspect, all members in the group are suspects. 
However, they find that it is not easy to identify all the suspects when a student is recognized as a suspect. Your job is to write a program which finds all the suspects.

Input

The input file contains several cases. Each test case begins with two integers n and m in a line, where n is the number of students, and m is the number of groups. You may assume that 0 < n <= 30000 and 0 <= m <= 500. Every student is numbered by a unique integer between 0 and n−1, and initially student 0 is recognized as a suspect in all the cases. This line is followed by m member lists of the groups, one line per group. Each line begins with an integer k by itself representing the number of members in the group. Following the number of members, there are k integers representing the students in this group. All the integers in a line are separated by at least one space. 
A case with n = 0 and m = 0 indicates the end of the input, and need not be processed.

Output

For each case, output the number of suspects in one line.

Sample Input

100 4
2 1 2
5 10 13 11 12 14
2 0 1
2 99 2
200 2
1 5
5 1 2 3 4 5
1 0
0 0

Sample Output

4
1
1
解题思路:统计和0在一组的成员,以及与0在一组的成员在一组的成员,我们在并查集的基础上加上一个ans数组,来记录同族中的成员个数。
以便如果是一个组的可以相加来统计总的个数,最后输出与0相关联的个数。
代码:
#include<iostream>
#include<cstdio>
using namespace std;
const int N=50010;
int s[N];
int n,m;
int ans[N];
int find_set(int root)
{
int son,temp;
son=root;
while(root!=s[root])
root=s[root];
while(son!=root)
{
temp=s[son];
s[son]=root;
son=temp;
}
return root;
}
void join(int x,int y)
{
x=find_set(x);
y=find_set(y);
if(x!=y)
{
s[x]=y;
ans[y]+=ans[x];
}
}
int main()
{
int i,j,k,tem,tem2;
while(scanf("%d%d",&n,&m))
{
if(n==0&&m==0)
break;
for(i=0;i<=n-1;i++)
{
s[i]=i;
ans[i]=1;
}
while(m--)
{
scanf("%d",&k);
cin>>tem;
for(i=0;i<k-1;i++)
{
cin>>tem2;
join(tem,tem2);
}
}
cout<<ans[find_set(0)]<<endl;
}
return 0;
}

4.​​D - Find them, Catch them​

 

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 belongs to. The present question is, given two criminals; do they belong to a same clan? You must give your judgment based on incomplete information. (Since the gangsters are always acting secretly.) 

Assume N (N <= 10^5) criminals are currently in Tadu City, numbered from 1 to N. And of course, at least one of them belongs to Gang Dragon, and the same for Gang Snake. You will be given M (M <= 10^5) messages in sequence, which are in the following two kinds: 

1. D [a] [b] 
where [a] and [b] are the numbers of two criminals, and they belong to different gangs. 

2. A [a] [b] 
where [a] and [b] are the numbers of two criminals. This requires you to decide whether a and b belong to a same gang. 

Input

The first line of the input contains a single integer T (1 <= T <= 20), the number of test cases. Then T cases follow. Each test case begins with a line with two integers N and M, followed by M lines each containing one message as described above.

Output

For each message "A [a] [b]" in each case, your program should give the judgment based on the information got before. The answers might be one of "In the same gang.", "In different gangs." and "Not sure yet."

Sample Input

1
5 5
A 1 2
D 1 2
A 1 2
D 2 4
A 1 4

Sample Output

Not sure yet.
In different gangs.
In the same gang.
解题思路:龙蛇帮,让我们判断他们是同一帮派还是不同帮派,或者不确定。我们加入了一个con数组来存储他的对手(就是跟他不是一个帮派的)
对于输入的两个不同帮派的的数,先判断他们的con是否为0,若是,则直接将该con值==对手;若不为0,则代表他已经有对手,那么现在的对手应该和已有的对手是一个帮派的
所以将二者结合到一起。同理在对另一个对手进行这样的判断。
代码:
#include<iostream>
#include<cstdio>
using namespace std;
const int N=100009;
int pre[N],con[N];

int find_set(int root)
{
int son,temp;
son=root;
while(root!=pre[root])
root=pre[root];
while(son!=root)
{
temp=pre[son];
pre[son]=root;
son=temp;
}
return root;
}
void join(int x,int y)
{
x=find_set(x);
y=find_set(y);
if(x!=y)
pre[x]=y;
}
int main()
{
int n,m,i,j,t,a,b;
char c;
cin>>t;
while(t--)
{
cin>>n>>m;
for(i=1;i<=n;i++)
{
pre[i]=i;
con[i]=0;
}
while(m--)
{
getchar();
scanf("%c%d%d",&c,&a,&b);
if(c=='D')
{
if(con[a]==0)
con[a]=b;
else
join(con[a],b);

if(con[b]==0)
con[b]=a;
else
join(con[b],a);
}
else
{
if(find_set(a)==find_set(b))
cout<<"In the same gang."<<endl;
else if(find_set(con[a])==find_set(b))
cout<<"In different gangs."<<endl;
else
cout<<"Not sure yet."<<endl;
}
}
}
return 0;
}

5.​​E - Wireless Network​

 

An earthquake takes place in Southeast Asia. The ACM (Asia Cooperated Medical team) have set up a wireless network with the lap computers, but an unexpected aftershock attacked, all computers in the network were all broken. The computers are repaired one by one, and the network gradually began to work again. Because of the hardware restricts, each computer can only directly communicate with the computers that are not farther than d meters from it. But every computer can be regarded as the intermediary of the communication between two other computers, that is to say computer A and computer B can communicate if computer A and computer B can communicate directly or there is a computer C that can communicate with both A and B. 

In the process of repairing the network, workers can take two kinds of operations at every moment, repairing a computer, or testing if two computers can communicate. Your job is to answer all the testing operations. 

Input

The first line contains two integers N and d (1 <= N <= 1001, 0 <= d <= 20000). Here N is the number of computers, which are numbered from 1 to N, and D is the maximum distance two computers can communicate directly. In the next N lines, each contains two integers xi, yi (0 <= xi, yi <= 10000), which is the coordinate of N computers. From the (N+1)-th line to the end of input, there are operations, which are carried out one by one. Each line contains an operation in one of following two formats: 
1. "O p" (1 <= p <= N), which means repairing computer p. 
2. "S p q" (1 <= p, q <= N), which means testing whether computer p and q can communicate. 

The input will not exceed 300000 lines. 

Output

For each Testing operation, print "SUCCESS" if the two computers can communicate, or "FAIL" if not.

Sample Input

4 1
0 1
0 2
0 3
0 4
O 1
O 2
O 4
S 1 4
O 3
S 1 4

Sample Output

FAIL
SUCCESS
解题思路:我们首先解题时,会想到要把已修复的并且在距离范围内的合并到一起,如果询问时,两者的祖先不一样代表二者不能相连,否则可以。
对于距离,我们先用X,Y数组来存储坐标,再建立dis[][]数组来存储任意两个点之间的距离,并且在设置一个flag数组,代表已修复的网络。每修复一个
网络,就查看该网络能否与其它已修复的网络相连,如果能就合并到一起。
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int pre[1010];
int X[1010],Y[1010];
double dis[1010][1010];
bool flag[1010];
int n,d;
void init()
{
for(int i=1;i<=n;i++)
{
pre[i]=i;
flag[i]=false;
}
}
int findx(int x)
{
if(x==pre[x])
return x;
pre[x]=findx(pre[x]);
return pre[x];
}
void join(int a,int b)
{
int pa=findx(a);
int pb=findx(b);
if(pa!=pb)
pre[pa]=pb;
}
int main()
{
int i,j,x,y;
scanf("%d%d",&n,&d);
for(i=1;i<=n;i++)
{
scanf("%d%d",&X[i],&Y[i]);
}
for(i=1;i<=n;i++)
{
for(j=i+1;j<=n;j++)
{
dis[i][j]=dis[j][i]=sqrt(pow(abs(X[i]-X[j]),2)+pow(abs(Y[i]-Y[j]),2));
}
}
init();
char s[100];
while(scanf("%s",s)!=EOF)
{
if(s[0]=='O')
{
int k;
scanf("%d",&k);
if(flag[k])
continue;
flag[k]=true;
for(i=1;i<=n;i++)
{
if(flag[i]&&dis[k][i]<=d)
{
join(k,i);
}
}
}
else if(s[0]=='S')
{
int k1;
int k2;
scanf("%d%d",&k1,&k2);
if(findx(k1)==findx(k2))
printf("SUCCESS\n");
else
printf("FAIL\n");
}
}
return 0;
}