并查集训练题解(A-E)

训练链接:http://acm.njupt.edu.cn/vjudge/contest/view.action?cid=173#overview



A题 Ubiquitous Religions
Time Limit: 5000MS
Memory Limit: 65536K
Total Submissions: 25461
Accepted: 12576

Description

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

Hint

Huge input, scanf is recommended.

Source

Alberta Collegiate Programming Contest 2003.10.18

题目链接:http://poj.org/problem?id=2524

题目大意:有n个地方,m组关系,每组关系表示两个地方的信仰相同,问一共有多少种不同的信仰

题目分析:裸并查集,贴模板过的题

#include 
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);
    }
}




B题 The Suspects
Time Limit: 1000MS   Memory Limit: 20000K
Total Submissions: 23748   Accepted: 11584

Description

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

Source

Asia Kaohsiung 2003

题目链接: http://poj.org/problem?id=1611

题目大意:n个人,m组关系,每组关系中有k个人,与0相关的均为嫌疑人,求有多少嫌疑人

题目分析:并查集,贴模板,最后找祖先是0的数个数

#include 
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);
    }
}



C题 Find them, Catch them
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 32909   Accepted: 10158

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 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.

Source

POJ Monthly--2004.07.18

题目链接: http://poj.org/problem?id=1703

题目大意: 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 
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");
            }
        }
    }
}



D题 A Bug's Life
Time Limit: 10000MS   Memory Limit: 65536K
Total Submissions: 29242   Accepted: 9538

Description

Background
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

The first line of the input contains the number of scenarios. Each scenario starts with one line giving the number of bugs (at least one, and up to 2000) and the number of interactions (up to 1000000) separated by a single space. In the following lines, each interaction is given in the form of two distinct bug numbers separated by a single space. Bugs are numbered consecutively starting from one.

Output

The output for every scenario is a line containing "Scenario #i:", where i is the number of the scenario starting at 1, followed by one line saying either "No suspicious bugs found!" if the experiment is consistent with his assumption about the bugs' sexual behavior, or "Suspicious bugs found!" if Professor Hopper's assumption is definitely wrong.

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

Huge input,scanf is recommended.

Source

TUD Programming Contest 2005, Darmstadt, Germany

题目链接: http://poj.org/problem?id=2492

题目大意:输入Scenario个数,每组有n个bug,有m对关系,每对关系表示两个性别不同,判断会不会出现矛盾

题目分析:a和b不同,b和c不同,若a和c再不同则出现矛盾,带权并查集基础应用,比C题还简单,不在同一集合则合并并改变两根偏移量,在同一集合只需要判断偏移量是否相同即可,若相同则说明出现了矛盾。

#include 
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");
    }
}



E题 Is It A Tree?
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 22910   Accepted: 7860

Description

A tree is a well-known data structure that is either empty (null, void, nothing) or is a set of one or more nodes connected by directed edges between nodes satisfying the following properties.

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.
并查集训练题解(A-E)_第1张图片

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

The input will consist of a sequence of descriptions (test cases) followed by a pair of negative integers. Each test case will consist of a sequence of edge descriptions followed by a pair of zeroes Each edge description will consist of a pair of integers; the first integer identifies the node from which the edge begins, and the second integer identifies the node to which the edge is directed. Node numbers will always be greater than zero.

Output

For each test case display the line "Case k is a tree." or the line "Case k is not a tree.", where k corresponds to the test case number (they are sequentially numbered starting with 1).

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

North Central North America 1997


题目链接: http://poj.org/problem?id=1308

题目大意:给你一些点的连接关系(第一个是起点,第二个是终点),判断这些点和边能否构成一棵树

题目分析:本 用并查集,把每个起点当作祖先,树的性质是边数加1等于点数,还要判段是否成环

#include 
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++);
    }     
}




你可能感兴趣的:(ACM,并查集)