[kuangbin带你飞]专题五 并查集

并查集的介绍可以看下https://www.cnblogs.com/jkzr/p/10290488.html

A - Wireless Network POJ - 2236

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

题意:给你N台电脑,编号从1到N。一个数字,表示两台计算机的最大通信距离,超过这个距离就无法进行通信。然后分别告诉这些电脑的坐标。
接下来有两种操作,第一种O表示这点电脑修好,第二种S,表示测试这两台电脑能不能进行正常的通信。
思路:并查集的简单应用,对每次修好的电脑对其它已经修好的电脑遍历,如果距离小于等于最大通信距离就将他们合并。之后判断2台电脑是不是一个集合中就可以了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
typedef long long LL;
const int maxn=1005;
int n,d;
int fa[maxn];
bool vis[maxn];
struct Node
{
    int x,y;
}node[maxn];
void init()
{
    for(int i=0;i<=n;i++)
        fa[i]=i;
    memset(vis,false,sizeof(vis));
}
int findd(int x)
{
    if(x==fa[x])
        return fa[x];
    else
        return fa[x]=findd(fa[x]);
}
void join(int x,int y)
{
    int fx=findd(x),fy=findd(y);
    if(fx!=fy)
        fa[fx]=fy;
}
int getdist(int a,int b)
{
    return (node[a].x-node[b].x)*(node[a].x-node[b].x)+(node[a].y-node[b].y)*(node[a].y-node[b].y);
}
int main()
{
    scanf("%d %d",&n,&d);
    init();
    for(int i=1;i<=n;i++)
        scanf("%d %d",&node[i].x,&node[i].y);
    char c;int p,q;
    while(cin>>c)
    {
        if(c=='O')
        {
            scanf("%d",&p);
            vis[p]=true;
            for(int i=1;i<=n;i++)
            {
                if(vis[i]&&i!=p)
                {
                    if(getdist(i,p)<=d*d)
                        join(i,p);
                }
            }
        }
        else
        {
            scanf("%d %d",&p,&q);
            if(findd(p)==findd(q))
                printf("SUCCESS\n");
            else
                printf("FAIL\n");
        }
    }
    return 0;
}

B - The Suspects POJ - 1611

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
题意:有很多组学生,在同一个组的学生经常会接触,也会有新的同学的加入。但是SARS是很容易传染的,只要在改组有一位同学感染SARS,那么该组的所有同学都被认为得了SARS。
计算出有多少位学生感染SARS了。假定编号为0的同学是得了SARS的。
思路:我们把在一个组的学生合并到同一个集合里面。用一个cnt[]数组记录每一个以当前下标为根节点的集合的个体数目,最后输出0号的根节点对应的cnt值,就是0号学生所在团体的人数,也就是我么要求的答案。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
typedef long long LL;
const int maxn=30005;
int n,m;
int fa[maxn];
int cnt[maxn];
void init()
{
    for(int i=0;i<=n;i++)
    {
        fa[i]=i;
        cnt[i]=1;
    }
}
int findd(int x)
{
    if(x==fa[x])
        return fa[x];
    else
        return fa[x]=findd(fa[x]);
}
void join(int x,int y)
{
    int fx=findd(x),fy=findd(y);
    if(fx!=fy)
    {
        fa[fx]=fy;
        cnt[fy]+=cnt[fx];
    }
}
int main()
{
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        if(n==0&&m==0)
            break;
        init();
        while(m--)
        {
            int num,a,b;
            scanf("%d",&num);
            scanf("%d",&a);
            for(int i=1;i<num;i++)
            {
                scanf("%d",&b);
                join(a,b);
            }
        }
        printf("%d\n",cnt[findd(0)]);
    }
    return 0;
}

C - How Many Tables HDU - 1213 

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. 

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

题意:Ignatius的生日,他邀请了许多朋友。现在是吃晚饭的时间,Ignatius想知道他至少需要准备多少桌。必须注意的是,并非所有的朋友都相互认识对方,有的人不愿意和陌生人坐在一桌。
针对此问题的一个重要的规则是,如果我告诉你A知道B,B知道C,这意味着,A和C认识对方,这样他们就可以留在一个桌子。
但是如果我告诉你,A知道B,B知道C,D知道E,那么ABC可以坐在一起,DE就得另外再坐一桌了。你的任务是请根据输入的朋友之间的关系,帮助Ignatius 求出需要安排多少桌。
思路:我们还是一样用开一个cnt[]数组,初始值为1,代表一个人一个桌子。后面添加认识的条件的时候如果两个人认识的话我们就合并,并把儿子的cnt值职位0,这样的话到最后统计所有的cnt的值就知道要准备多少桌子了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
typedef long long LL;
const int maxn=30005;
int T,n,m,ans;
int fa[maxn];
int cnt[maxn];
void init()
{
    for(int i=0;i<=n;i++)
    {
        fa[i]=i;
        cnt[i]=1;
    }
}
int findd(int x)
{
    if(x==fa[x])
        return fa[x];
    else
        return fa[x]=findd(fa[x]);
}
void join(int x,int y)
{
    int fx=findd(x),fy=findd(y);
    if(fx!=fy)
    {
        fa[fx]=fy;
        cnt[fx]=0;
    }
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&m);
        init();
        while(m--)
        {
            int a,b;
            scanf("%d %d",&a,&b);
            join(a,b);
        }
        ans=0;
        for(int i=1;i<=n;i++)
            ans+=cnt[i];
        printf("%d\n",ans);
    }
    return 0;
}

D - How Many Answers Are Wrong HDU - 3038 

TT and FF are ... friends. Uh... very very good friends -________-b 

FF is a bad boy, he is always wooing TT to play the following game with him. This is a very humdrum game. To begin with, TT should write down a sequence of integers-_-!!(bored). 

Then, FF can choose a continuous subsequence from it(for example the subsequence from the third to the fifth integer inclusively). After that, FF will ask TT what the sum of the subsequence he chose is. The next, TT will answer FF's question. Then, FF can redo this process. In the end, FF must work out the entire sequence of integers. 

Boring~~Boring~~a very very boring game!!! TT doesn't want to play with FF at all. To punish FF, she often tells FF the wrong answers on purpose. 

The bad boy is not a fool man. FF detects some answers are incompatible. Of course, these contradictions make it difficult to calculate the sequence. 

However, TT is a nice and lovely girl. She doesn't have the heart to be hard on FF. To save time, she guarantees that the answers are all right if there is no logical mistakes indeed. 

What's more, if FF finds an answer to be wrong, he will ignore it when judging next answers. 

But there will be so many questions that poor FF can't make sure whether the current answer is right or wrong in a moment. So he decides to write a program to help him with this matter. The program will receive a series of questions from FF together with the answers FF has received from TT. The aim of this program is to find how many answers are wrong. Only by ignoring the wrong answers can FF work out the entire sequence of integers. Poor FF has no time to do this job. And now he is asking for your help~(Why asking trouble for himself~~Bad boy) 

InputLine 1: Two integers, N and M (1 <= N <= 200000, 1 <= M <= 40000). Means TT wrote N integers and FF asked her M questions. 

Line 2..M+1: Line i+1 contains three integer: Ai, Bi and Si. Means TT answered FF that the sum from Ai to Bi is Si. It's guaranteed that 0 < Ai <= Bi <= N. 

You can assume that any sum of subsequence is fit in 32-bit integer. 
OutputA single line with a integer denotes how many answers are wrong.Sample Input

10 5
1 10 100
7 10 28
1 3 32
4 6 41
6 6 1

Sample Output

1
题意:给出区间[1,n],下面有m组数据,l r v代表区间[l,r]之和为v,每输入一组数据,判断此组条件是否与前面冲突 ,最后输出与前面冲突的数据的个数。
思路:这是一个区间统计的题。我们利用一个sum[]数组保存从某点到其祖先节点距离。注意需要对所有值统计设置相同的初值,但初值的大小一般没有影响。
对区间[l, r]进行记录时,实际上是对 (l-1, r]操作,即l = l - 1。
剩下的就是一个转移的理解,可以看这个 https://www.cnblogs.com/liyinggang/p/5327055.html。

截下屏看下。

  
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
typedef long long LL;
const int maxn=200005;
int n,m,a,b,d,ans;
int fa[maxn];
int cnt[maxn];
void init()
{
    for(int i=0;i<=n;i++)
    {
        fa[i]=i;
        cnt[i]=0;
    }
    ans=0;
}
int findd(int x)
{
    if(x==fa[x])
        return fa[x];
    else
    {
        int t=fa[x];
        fa[x]=findd(fa[x]);
        cnt[x]+=cnt[t];
        return fa[x];
    }
}
bool join(int x,int y,int d)
{
    int fx=findd(x),fy=findd(y);
    if(fx==fy)
    {
        if(cnt[x]-cnt[y]!=d)
        {
//            cout<<"666"<<endl;
            return true;
        }
    }
    else
    {
//        if(fx<fy)
//        {
            fa[fx]=fy;
            cnt[fx]=-cnt[x]+cnt[y]+d;
//        }
//        else
//        {
//            fa[fy]=fx;
//            cnt[fy]=-cnt[y]+cnt[x]-d;
//        }
    }
    return 0;
}
int main()
{
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        init();
        while(m--)
        {
            scanf("%d %d %d",&a,&b,&d);
            a--;
            if(join(a,b,d))
                ans++;
//            for(int i=0;i<=n;i++)
//                cout<<fa[i]<<" ";
//            cout<<endl;
        }
        printf("%d\n",ans);
    }
    return 0;
}

E - 食物链 POJ - 1182

动物王国中有三类动物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),输出假话的总数。 

Input

第一行是两个整数N和K,以一个空格分隔。 
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。 
若D=1,则表示X和Y是同类。 
若D=2,则表示X吃Y。

Output

只有一个整数,表示假话的数目。

Sample Input

100 7
1 101 1 
2 1 2
2 2 3 
2 3 3 
1 1 3 
2 3 1 
1 5 5

Sample Output

3
题意:看题面很好理解的了。
思路:经典带权并查集。对于这三种种类,同类可以用0表示,其他两种分别用1表示该结点被父节点吃,2表示该节点吃父节点。 (要注意这个不是随便分配的,要根据题意推导)
该题之所以能用并查集进行路径压缩,是因为存在A吃B,B吃C,C吃A的三角关系。
这是我们能在路径压缩中使用num[x] = (num[x] + num[fa]) % 3和更新时使用num[fb] = (3 - num[v] + num[u] + (p - 1)) % 3的原因(否则就是一种链式关系了)。
可以看上一题的内容,那个向量的内容,都是一样的。路径压缩的时候更新关系和合并的时候更新关系
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
typedef long long LL;
const int maxn=50005;
int n,k,d,x,y,ans;
int fa[maxn],relation[maxn];
void init()
{
    for(int i=0;i<=n;i++)
    {
        fa[i]=i;
        relation[i]=0;
    }
}
int findd(int x)
{
    if(x==fa[x])
        return fa[x];
    else
    {
        int temp=findd(fa[x]);
        relation[x]=(relation[x]+relation[fa[x]]+3)%3;
        fa[x]=temp;
        return fa[x];
    }
}
void join(int d,int x,int y)
{
    int fx=findd(x),fy=findd(y);
    if(fx!=fy)
    {
        fa[fx]=fy;
        relation[fx]=(-relation[x]+d+relation[y]+3)%3;
    }
    else
    {
        if((relation[x]-relation[y]+3)%3!=d)
            ans++;
    }
}

int main()
{
    scanf("%d %d",&n,&k);
    ans=0;
    init();
    while(k--)
    {
        scanf("%d %d %d",&d,&x,&y);
        if(x>n||y>n)
        {
            ans++;
            continue;
        }
        if(d==2&&x==y)
        {
            ans++;
            continue;
        }
        join(d-1,x,y);
    }
    printf("%d\n",ans);
    return 0;
}

G - Supermarket POJ - 1456 

A supermarket has a set Prod of products on sale. It earns a profit px for each product x∈Prod sold by a deadline dx that is measured as an integral number of time units starting from the moment the sale begins. Each product takes precisely one unit of time for being sold. A selling schedule is an ordered subset of products Sell ≤ Prod such that the selling of each product x∈Sell, according to the ordering of Sell, completes before the deadline dx or just when dx expires. The profit of the selling schedule is Profit(Sell)=Σ x∈Sellpx. An optimal selling schedule is a schedule with a maximum profit. 
For example, consider the products Prod={a,b,c,d} with (pa,da)=(50,2), (pb,db)=(10,1), (pc,dc)=(20,2), and (pd,dd)=(30,1). The possible selling schedules are listed in table 1. For instance, the schedule Sell={d,a} shows that the selling of product d starts at time 0 and ends at time 1, while the selling of product a starts at time 1 and ends at time 2. Each of these products is sold by its deadline. Sell is the optimal schedule and its profit is 80. 

Write a program that reads sets of products from an input text file and computes the profit of an optimal selling schedule for each set of products. 

Input

A set of products starts with an integer 0 <= n <= 10000, which is the number of products in the set, and continues with n pairs pi di of integers, 1 <= pi <= 10000 and 1 <= di <= 10000, that designate the profit and the selling deadline of the i-th product. White spaces can occur freely in input. Input data terminate with an end of file and are guaranteed correct.

Output

For each set of products, the program prints on the standard output the profit of an optimal selling schedule for the set. Each result is printed from the beginning of a separate line.

Sample Input

4  50 2  10 1   20 2   30 1

7  20 1   2 1   10 3  100 2   8 2
   5 20  50 10

Sample Output

80
185
题意:买卖N件东西,每件东西都有个截止时间,在截止时间之前买都可以,而每个单位时间只能买一件。问最大获利。
思路:先用sort快排,把最值钱的商品放在第一个,然后先从当前最值钱的开始算,如果当天可以卖的话,就拿一天卖掉,
如果有商品占了那一天,就往前一天寻找,并查集在这里就作为最靠近其保质期当天的那一天,如果其根为0,则表示该商品没有空闲的天卖出。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=10005;
int n;
int ans;
int fa[maxn];
struct Node
{
    int p,d;
}node[maxn];
bool cmp(Node a,Node b)
{
    return a.p>b.p;
}
int findd(int x)
{
    if(fa[x]==-1)
        return x;
    else
    {
        fa[x]=findd(fa[x]);
        return fa[x];
    }
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++)
            scanf("%d %d",&node[i].p,&node[i].d);
        sort(node+1,node+1+n,cmp);
        memset(fa,-1,sizeof(fa));
        ans=0;
        for(int i=1;i<=n;i++)
        {
            int temp=findd(node[i].d);
            if(temp>0)
            {
                ans+=node[i].p;
                fa[temp]=temp-1;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

J - A Bug's Life POJ - 2492 

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!

题意:给定n只虫子 不同性别的可以在一起 相同性别的不能在一起。给你m对虫子 判断中间有没有同性别在一起的;
思路:食物链的简单版,我们可以用rank[x]记录x与其父亲节点的关系, rank[x]=0表同性, rank[x]=1表异性;假设前面的教授判断都是正确的, 若后面存在与前面判断矛盾的数据,那么教授判断有误;
注意输出的格式,坑。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
typedef long long LL;
const int maxn=2005;
int T,n,m,a,b;
bool flag;
int fa[maxn];
int relation[maxn];
void init()
{
    for(int i=0;i<=n;i++)
    {
        fa[i]=i;
        relation[i]=0;
    }
    flag=false;
}
int findd(int x)
{
    if(x==fa[x])
        return fa[x];
    else
    {
        int temp=findd(fa[x]);
        relation[x]=(relation[fa[x]]+relation[x])%2;
        fa[x]=temp;
        return fa[x];
    }
}
bool join(int x,int y)
{
    int fx=findd(x),fy=findd(y);
    if(fx!=fy)
    {
        fa[fx]=fy;
        relation[fx]=(-relation[x]+relation[y]+1)%2;
    }
    else
    {
        if((relation[x]+relation[y])%2==0)
            return true;
    }
    return false;
}
int main()
{
    int casee=1;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&m);
        init();
        while(m--)
        {
            scanf("%d %d",&a,&b);
            if(join(a,b))
                flag=true;
        }
        printf("Scenario #%d:\n",casee++);
        if(flag)
            printf("Suspicious bugs found!\n\n");
        else
            printf("No suspicious bugs found!\n\n");
    }
    return 0;
}

L - Connections in Galaxy War ZOJ - 3261 

In order to strengthen the defense ability, many stars in galaxy allied together and built many bidirectional tunnels to exchange messages. However, when the Galaxy War began, some tunnels were destroyed by the monsters from another dimension. Then many problems were raised when some of the stars wanted to seek help from the others.

In the galaxy, the stars are numbered from 0 to N-1 and their power was marked by a non-negative integer pi. When the star A wanted to seek help, it would send the message to the star with the largest power which was connected with star A directly or indirectly. In addition, this star should be more powerful than the star A. If there were more than one star which had the same largest power, then the one with the smallest serial number was chosen. And therefore, sometimes star A couldn't find such star for help.

Given the information of the war and the queries about some particular stars, for each query, please find out whether this star could seek another star for help and which star should be chosen.

Input

There are no more than 20 cases. Process to the end of file.

For each cases, the first line contains an integer N (1 <= N <= 10000), which is the number of stars. The second line contains N integers p0p1, ... , pn-1 (0 <= pi <= 1000000000), representing the power of the i-th star. Then the third line is a single integer M (0 <= M <= 20000), that is the number of tunnels built before the war. Then M lines follows. Each line has two integers ab (0 <= ab <= N - 1, a != b), which means star a and star b has a connection tunnel. It's guaranteed that each connection will only be described once.

In the (M + 2)-th line is an integer Q (0 <= Q <= 50000) which is the number of the information and queries. In the following Q lines, each line will be written in one of next two formats.

"destroy a b" - the connection between star a and star b was destroyed by the monsters. It's guaranteed that the connection between star a and star b was available before the monsters' attack.

"query a" - star a wanted to know which star it should turn to for help

There is a blank line between consecutive cases.

Output

For each query in the input, if there is no star that star a can turn to for help, then output "-1"; otherwise, output the serial number of the chosen star.

Print a blank line between consecutive cases.

Sample Input

2
10 20
1
0 1
5
query 0
query 1
destroy 0 1
query 0
query 1

Sample Output

1
-1
-1
-1

题意:给你一些点,还有一些边,每个点上都有一个权值,然后有一些询问,分为两种,
query a 询问与a直接或者间接想连的点中最大权值的是那个点,输出那个点,如果那个点的权值小于等于a的权值,那么就输出-1,还有另一种操作就是destroy a b意思是删除a b的关系。
思路:逆向并查集,把没有删除的边先加入并查集,一个集合内表示连通的,根结点为权值最大的点。然后对于查询离线读入,从最后开始操作,对于删除的点,然后重新加入到并查集中,更新最值。
查询的时候便是查询根结点的值是否大于自身的值。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#include <stack>
#define PI pair<int, int>
using namespace std;
const int maxn=10005;
int n,m,Q;
int x,y;
int a[maxn],fa[maxn];
struct Node
{
    int x,y;
}node[maxn*5];
struct Key
{
    char s[20];
    int x,y;
}q[maxn*5];
void init()
{
    for(int i=0;i<=n;i++)
        fa[i]=i;
}
int findd(int x)
{
    if(x==fa[x])
        return fa[x];
    else
        return fa[x]=findd(fa[x]);
}
void join(int x,int y)
{
    int fx=findd(x),fy=findd(y);
    if(a[fx]==a[fy])
    {
        if(fx<fy)
            fa[fy]=fx;
        else
            fa[fx]=fy;
    }
    else if(a[fx]<a[fy])
        fa[fx]=fy;
    else
        fa[fy]=fx;
}
int main()
{
    int k=0;
    while(scanf("%d",&n)!=EOF)
    {
        if(k!=0)
            printf("\n");
        k++;
        init();
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d %d",&node[i].x,&node[i].y);
            if(node[i].x>node[i].y)
                swap(node[i].x,node[i].y);
        }
        scanf("%d",&Q);
        map<PI, int>ha;
        for(int i=1;i<=Q;i++)
        {
            scanf("%s",q[i].s);
            if(q[i].s[0]=='d')
            {
                scanf("%d %d",&q[i].x,&q[i].y);
                if(q[i].x>q[i].y)
                    swap(q[i].x,q[i].y);
                PI pi=make_pair(q[i].x,q[i].y);
                ha[pi]=1;
            }
            else
                scanf("%d",&q[i].x);
        }

        for(int i=1;i<=m;i++)
        {
            if(!ha.count(make_pair(node[i].x,node[i].y))) {
                join(node[i].x,node[i].y);
            }
        }

        stack<int>ans;
        for(int i=Q;i>0;i--)
        {
            if(q[i].s[0]=='q')
            {
                int r=findd(q[i].x);
                if(a[r]<=a[q[i].x])
                    ans.push(-1);
                else
                    ans.push(r);
            }
            else
                join(q[i].x,q[i].y);
        }
        while(!ans.empty())
        {
            printf("%d\n",ans.top());
            ans.pop();
        }
    }
    return 0;
}

M - 小希的迷宫 HDU - 1272

上次Gardon的迷宫城堡小希玩了很久(见Problem B),现在她也想设计一个迷宫让Gardon来走。但是她设计迷宫的思路不一样,首先她认为所有的通道都应该是双向连通的,就是说如果有一个通道连通了房间A和B,那么既可以通过它从房间A走到房间B,也可以通过它从房间B走到房间A,为了提高难度,小希希望任意两个房间有且仅有一条路径可以相通(除非走了回头路)。小希现在把她的设计图给你,让你帮忙判断她的设计图是否符合她的设计思路。比如下面的例子,前两个是符合条件的,但是最后一个却有两种方法从5到达8。 
 

Input输入包含多组数据,每组数据是一个以0 0结尾的整数对列表,表示了一条通道连接的两个房间的编号。房间的编号至少为1,且不超过100000。每两组数据之间有一个空行。 
整个文件以两个-1结尾。 
Output对于输入的每一组数据,输出仅包括一行。如果该迷宫符合小希的思路,那么输出"Yes",否则输出"No"。 
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

Yes
Yes
No
思路:并查集与图的结合,要判断图是否连通,即检查新给的两个点的father是否相同但是一开始没有考虑到连通性,要检查根节点的数量,即i==father[i]的点为1即判断该图是否为连通无环图
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
typedef long long LL;
const int maxn=100005;
int n,m;
bool flag;
int fa[maxn];
bool vis[maxn];
void init()
{
    for(int i=0;i<maxn;i++)
    {
        fa[i]=i;
        vis[i]=false;
    }
}
int findd(int x)
{
    if(x==fa[x])
        return fa[x];
    else
        return fa[x]=findd(fa[x]);
}
void join(int x,int y)
{
    int fx=findd(x),fy=findd(y);
    if(fx!=fy)
        fa[fx]=fy;
}

int main()
{
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        if(n==-1&&m==-1)
            break;
        init(); 
        flag=false;
        while(1)
        {
            if(n==0&&m==0)
                break;
            if(findd(n)==findd(m))
                flag=true;
            join(n,m);
            vis[n]=vis[m]=true;
            scanf("%d %d",&n,&m);
        }
        if(flag)
            printf("No\n");
        else
        {
            int cnt=0;
            for(int i=0;i<maxn;i++)
            {
                if(vis[i]&&fa[i]==i)
                    cnt++;
            }
            if(cnt>1)
                printf("No\n");
            else
                printf("Yes\n");
        }
    }
    return 0;
}

N - Is It A Tree? POJ - 1308 

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. 

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.

和上一题一样的思路和解法。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
typedef long long LL;
const int maxn=100005;
int n,m;
bool flag;
int fa[maxn];
bool vis[maxn];
void init()
{
    for(int i=0;i<maxn;i++)
    {
        fa[i]=i;
        vis[i]=false;
    }
}
int findd(int x)
{
    if(x==fa[x])
        return fa[x];
    else
        return fa[x]=findd(fa[x]);
}
void join(int x,int y)
{
    int fx=findd(x),fy=findd(y);
    if(fx!=fy)
        fa[fx]=fy;
}

int main()
{
    int casee=1;
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        if(n==-1&&m==-1)
            break;
        init();
        flag=false;
        while(1)
        {
            if(n==0&&m==0)
                break;
            if(findd(n)==findd(m))
                flag=true;
            join(n,m);
            vis[n]=vis[m]=true;
            scanf("%d %d",&n,&m);
        }
        if(flag)
            printf("Case %d is not a tree.\n",casee++);
        else
        {
            int cnt=0;
            for(int i=0;i<maxn;i++)
            {
                if(vis[i]&&fa[i]==i)
                    cnt++;
            }
            if(cnt>1)
                printf("Case %d is not a tree.\n",casee++);
            else
                printf("Case %d is a tree.\n",casee++);
        }
    }
    return 0;
}

 

 
posted @ 2019-01-21 15:41  从让帝到the_rang  阅读(452)  评论(0编辑  收藏  举报