1. 程式人生 > >並查集——分身法

並查集——分身法

試題描述
俗話說得好,敵人的敵人就是朋友。
現在有n個人,編號1至n,初始互不相識。接下來有m個操作,操作分為兩種:

(1)檢查x號和y號是否是朋友,若不是,則變成敵人
(2)詢問x號的朋友有多少個
請你針對每個操作中的詢問給出回答。
輸入
第一行兩個正整數n、m,表示人的數量和操作的數量。
接下來m行,依次描述輸入。每行的第一個整數為1或2表示操作種類。對於操作(1),隨後有兩個正整數x,y。對於操作(2),隨後一個正整數x。
輸出
輸出包含m行,對於操作(1),輸入’N’或”Y”,’N’表示x和y之前不是朋友,’Y’表示是朋友。對於操作(2),輸出x的朋友數量。
輸入示例
5 8
1 1 2
1 1 3
1 2 3
2 3
1 4 5
2 3
1 1 4
2 3
輸出示例
N
N
Y
1
N
1
N
2
其他說明
n,m<=300000。
對於80%的資料,不包含操作2。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;
const int maxn = 3e5+5;

int f[maxn*2];
int size1[maxn];
int findf(int k)
{
    int r,j;
    r=k;
    while(r!=f[r])
        r=f[r];
    j=k;
    while
(j!=r) { int gg=f[j]; f[j]=r; j=gg; } return r; } void merge1(int a,int b) { a=findf(a),b=findf(b); if(a!=b) { f[b]=a;size1[a]+=size1[b]; } } int main() { int n,m; scanf("%d %d",&n,&m); for(int i=0;i<=2*n;i++) f[i]=i; for
(int i=1;i<=n;i++) size1[i]=1; for(int i=0;i<m;i++) { int q,a,b; scanf("%d",&q); if(q==1) { scanf("%d %d",&a,&b); int root1=findf(a); int root2=findf(b); if(root1!=root2) { merge1(a,b+n); merge1(b,a+n); printf("N\n"); } else printf("Y\n"); } else if(q==2) { scanf("%d",&a); a=findf(a); printf("%d\n",size1[a]-1); } } }

試題描述
有N只動物分別編號為1,2,……,N。所有動物都屬於A,B,C中的一類。已知A能吃掉B,B能吃掉C,C能吃掉A。按順序給出下面的兩種資訊共K條:
第一種:x和y屬於同一類;
第二種:x吃y。
然而這些資訊可能會出錯,有可能有的資訊和之前給出的資訊矛盾,也有的資訊可能給出的x和y不在1到N的範圍內。求在K條資訊中有多少條是不正確的。計算過程中,我們將忽視諸如此類的錯誤資訊。

輸入
第一行兩個自然數,兩數間用一個空格分隔,分別表示N和K,接下來的K行,每行有三個數,第一個數為0或1,分別對應第一種或第二種,接著的兩個數,分別為該條資訊的x和y,三個數兩兩之間用一個空格分隔。
輸出
一個自然數,表示錯誤資訊的條數。
輸入示例
100 7
0 101 1
1 1 2
1 2 3
1 3 3
0 1 3
1 3 1
0 5 5
輸出示例
3
其他說明
資料範圍:1<=N<=50000,0<=K<=100000,其它說有輸入都不會超過100000.

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;
const int maxn = 5e4+5;
int f[maxn*3];
int findf(int k)
{
    int r,j;
    r=k;
    while(r!=f[r])
        r=f[r];
    j=k;
    while(j!=r)
    {
        int gg=f[j];
        f[j]=r;
        j=gg;
    }
    return r;
}
void merge1(int a,int b)
{
    a=findf(a),b=findf(b);
    if(a!=b)
    {
        f[b]=a;
    }
    return ;
}
int main()
{
    int n,k;
    scanf("%d %d",&n,&k);
    int ans =0;
    for(int i=0;i<=maxn*3;i++)
        f[i]=i;
    for(int i=0;i<k;i++)
    {
        int p,x,y;
        scanf("%d %d %d",&p,&x,&y);
        if(x>n||y>n) ans++;
        else
        {
            if(p==2&&x==y) ans++;
            else
            {
                if(p==1)
                {
                    if(findf(x)==findf(y+n)||findf(x)==findf(y+2*n)) ans++;
                    else {
                        merge1(x,y);
                        merge1(x+n,y+n);
                        merge1(x+2*n,y+2*n);
                    }
                }
                else
                {
                    if(findf(x)==findf(y)||findf(x)==findf(y+2*n)) ans++;//只用取其中代表的一個就好了,因為這個不成立,則其他的也不成立,這個成立的話,其他的也成立
                    else {
                        merge1(x,y+n);
                        merge1(x+n,y+2*n);
                        merge1(y,x+2*n);
                    }
                }
            }
        }
    }
     printf("%d\n",ans);
    return 0;
}