训练链接:http://acm.njupt.edu.cn/vjudge/contest/view.action?cid=173#overview
Time Limit: 5000MS | Memory Limit: 65536K | |
Total Submissions: 25461 | Accepted: 12576 |
Description
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
Output
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
Hint
Source
题目链接:http://poj.org/problem?id=2524
题目大意:有n个地方,m组关系,每组关系表示两个地方的信仰相同,问一共有多少种不同的信仰
题目分析:裸并查集,贴模板过的题
#include <cstdio>
int fa[50005];
void UF_set(int n)
{
for(int i = 1; i <= n; i++)
fa[i] = i;
}
int Find(int x)
{
return x == fa[x] ? x : fa[x] = Find(fa[x]);
}
void Union(int a, int b)
{
int r1 = Find(a);
int r2 = Find(b);
fa[r1] = r2;
}
int main()
{
int n, m, ans, ca = 1;
while(scanf("%d %d", &n, &m) != EOF && (m + n))
{
ans = 0;
UF_set(n);
int a, b;
for(int i = 0; i < m; i++)
{
scanf("%d %d", &a, &b);
Union(a, b);
}
for(int i = 1; i <= n; i++)
if(i == Find(i))
ans++;
printf("Case %d: %d\n", ca++, ans);
}
}
Time Limit: 1000MS | Memory Limit: 20000K | |
Total Submissions: 23748 | Accepted: 11584 |
Description
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
A case with n = 0 and m = 0 indicates the end of the input, and need not be processed.
Output
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
Source
题目链接: http://poj.org/problem?id=1611
题目大意:n个人,m组关系,每组关系中有k个人,与0相关的均为嫌疑人,求有多少嫌疑人
题目分析:并查集,贴模板,最后找祖先是0的数个数
#include <cstdio>
int const MAX = 30000 + 5;
int fa[MAX];
void UF_set(int n)
{
for(int i = 0; i < n; i++)
fa[i] = i;
}
int Find(int x)
{
return x == fa[x] ? x : fa[x] = Find(fa[x]);
}
void Union(int a, int b)
{
int r1 = Find(a);
int r2 = Find(b);
if(r1 == 0)
fa[r2] = r1;
else if(r2 == 0)
fa[r1] = r2;
else
fa[r1] = r2;
}
int main()
{
int n, m, ans;
while(scanf("%d %d", &n, &m) != EOF && (n + m))
{
ans = 0;
UF_set(n);
for(int i = 0; i < m; i++)
{
int get, fir, k;
scanf("%d %d", &k, &fir);
for(int j = 1; j < k; j++)
{
scanf("%d", &get);
Union(get, fir);
}
}
for(int i = 0; i < n; i++)
if(Find(i) == 0)
ans++;
printf("%d\n", ans);
}
}
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 32909 | Accepted: 10158 |
Description
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
Output
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.
Source
题目大意: n个罪犯,m组关系,有2个敌对帮派,D a b表示a,b在不同帮派,A a b表示询问a,b是否是在一个帮派。
题目分析:简单的种类并查集, 假设 x->y 偏移量0时 x和y同帮派, x->y 偏移量1时 x和y不同帮派
继续假设,x的当前集合根节点rx,y的当前集合根节点ry。如果rx和ry不相同,那么我们把ry合并到rx上,并且更新w[ry]值(注意:w[i]表示i当前集合根节点到i的偏移量)
此时 rx->ry = rx->x + x->y + y->ry,(向量运算)
上式进一步转化为:rx->ry = (w[x]+1-w[y])%2 = w[ry],(模2保证偏移量取值始终在[0,1])
接下来把这个想法再运用到路径压缩中:
rrx
| \
rx \
| /
| /
x
路径压缩过程中会将rx的父亲变为x的父亲,所以要改变相对关系,即偏移量。
rrx->rx+rx->x=rrx->x;
转换成:w[x]=(w[rx]+w[x])%2;
#include <cstdio>
int const MAX = 1e5 + 5;
int fa[MAX], w[MAX], n;
void UF_set()
{
for(int i = 1 ; i <= n; i++)
{
fa[i] = i;
w[i] = 0;
}
}
int Find(int x)
{
if(x == fa[x])
return x;
int tmp = fa[x];
fa[x] = Find(fa[x]);
w[x] = (w[x] + w[tmp]) % 2;
return fa[x];
}
void Union(int a, int b)
{
int r1 = Find(a);
int r2 = Find(b);
if(r1 != r2)
{
fa[r2] = r1;
w[r2] = (w[a] + 1 - w[b]) % 2;
}
}
int main()
{
int T, m;
scanf("%d", &T);
while(T--)
{
scanf("%d %d", &n, &m);
UF_set();
for(int i = 0; i < m; i++)
{
char ch[2];
int x, y;
scanf("%s %d %d", ch, &x, &y);
if(ch[0] == 'D')
Union(x, y);
if(ch[0] == 'A')
{
if(Find(x) == Find(y))
{
if(w[x] == w[y])
printf("In the same gang.\n");
else
printf("In different gangs.\n");
}
else
printf("Not sure yet.\n");
}
}
}
}
Time Limit: 10000MS | Memory Limit: 65536K | |
Total Submissions: 29242 | Accepted: 9538 |
Description
Professor Hopper is researching the sexual behavior of a rare species of bugs. He assumes that they feature two different genders and that they only interact with bugs of the opposite gender. In his experiment, individual bugs and their interactions were easy to identify, because numbers were printed on their backs.
Problem
Given a list of bug interactions, decide whether the experiment supports his assumption of two genders with no homosexual bugs or if it contains some bug interactions that falsify it.
Input
Output
Sample Input
2 3 3 1 2 2 3 1 3 4 2 1 2 3 4
Sample Output
Scenario #1: Suspicious bugs found! Scenario #2: No suspicious bugs found!
Hint
Source
题目链接: http://poj.org/problem?id=2492
题目大意:输入Scenario个数,每组有n个bug,有m对关系,每对关系表示两个性别不同,判断会不会出现矛盾
题目分析:a和b不同,b和c不同,若a和c再不同则出现矛盾,带权并查集基础应用,比C题还简单,不在同一集合则合并并改变两根偏移量,在同一集合只需要判断偏移量是否相同即可,若相同则说明出现了矛盾。
#include <cstdio>
int const MAX = 2005;
int fa[MAX], w[MAX];
int n;
bool flag;
void UF_set()
{
for(int i = 1; i <= n; i++)
{
fa[i] = i;
w[i] = 0;
}
flag = false;
}
int Find(int x)
{
if(x == fa[x])
return x;
int tmp = fa[x];
fa[x] = Find(fa[x]);
w[x] = (w[x] + w[tmp]) % 2;
return fa[x];
}
void Union(int a, int b)
{
int r1 = Find(a);
int r2 = Find(b);
if(r1 == r2 && w[a] == w[b])
flag = true;
else
{
fa[r2] = r1;
w[r2] = (w[a] + 1 - w[b]) % 2;
}
}
int main()
{
int T, m;
scanf("%d", &T);
for(int ca = 1; ca <= T; ca++)
{
scanf("%d %d", &n, &m);
UF_set();
for(int i = 0; i < m; i++)
{
int x, y;
scanf("%d %d", &x, &y);
if(!flag)
Union(x, y);
}
if(flag)
printf("Scenario #%d:\nSuspicious bugs found!\n", ca);
else
printf("Scenario #%d:\nNo suspicious bugs found!\n", ca);
if(ca != T)
printf("\n");
}
}
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 22910 | Accepted: 7860 |
Description
There is exactly one node, called the root, to which no directed edges point.
Every node except the root has exactly one edge pointing to it.
There is a unique sequence of directed edges from the root to each node.
For example, consider the illustrations below, in which nodes are represented by circles and edges are represented by lines with arrowheads. The first two of these are trees, but the last is not.
![](https://i-blog.csdnimg.cn/blog_migrate/e396b4b2ddd3a623af2a8c4f9d4a84ec.jpeg)
In this problem you will be given several descriptions of collections of nodes connected by directed edges. For each of these you are to determine if the collection satisfies the definition of a tree or not.
Input
Output
Sample Input
6 8 5 3 5 2 6 4 5 6 0 0 8 1 7 3 6 2 8 9 7 5 7 4 7 8 7 6 0 0 3 8 6 8 6 4 5 3 5 6 5 2 0 0 -1 -1
Sample Output
Case 1 is a tree. Case 2 is a tree. Case 3 is not a tree.
Source
题目链接: http://poj.org/problem?id=1308
题目大意:给你一些点的连接关系(第一个是起点,第二个是终点),判断这些点和边能否构成一棵树
题目分析:本 题用并查集,把每个起点当作祖先,树的性质是边数加1等于点数,还要判段是否成环
#include <cstdio>
int const MAX = 1e4 + 5;
int fa[MAX];
bool vis[MAX], flag;
int n, e; //点数和边数
void init()
{
flag = true;
n = 0, e = 0;
for(int i = 0; i < MAX; i++)
{
fa[i] = i;
vis[i] = false;
}
}
int Find(int x)
{
return x == fa[x] ? x : fa[x] = Find(fa[x]);
}
void Union(int a, int b)
{
int r1 = Find(a);
int r2 = Find(b);
if(r1 != r2)
fa[r2] = r1; //不能反!
else //若根相同,又a与b相连必然成环不是树
flag = false;
}
int main()
{
int a, b, ca = 1;
while(scanf("%d %d", &a ,&b) && (a + b) != -2)
{
if(a == 0 && b == 0)
{
printf("Case %d is a tree.\n", ca++);
continue;
}
init();
while(a + b)
{
Union(a, b);
if(!vis[a])
{
vis[a] = true;
n++;
}
if(!vis[b])
{
vis[b] = true;
n++;
}
e++;
scanf("%d %d", &a, &b);
}
if(n != e + 1)
flag = false;
if(flag)
printf("Case %d is a tree.\n", ca++);
else
printf("Case %d is not a tree.\n", ca++);
}
}