1. 程式人生 > 實用技巧 >2020牛客暑期多校第八場I-Interesting Computer Game(離散化+並查集)

2020牛客暑期多校第八場I-Interesting Computer Game(離散化+並查集)

CSDN食用連結:https://blog.csdn.net/qq_43906000/article/details/107797019
題目大意:給你T組資料,每組資料給你n對數,分別表示為\(a_i,b_i(1\leq a_i,b_i\leq 10^9)\),你每次都可以取其中的一個,但如果之前\(a_i\)已經被取過了,就不能再取了,\(b_i\)也是一樣的。那麼問你總共可以取多少種不同的數。\((T\leq 10,n\leq 1e5)\)

輸入
2
6
1 2
2 3
3 4
1 4
1 3
2 4
5
1 2
1 2
1 3
2 3
5 6

輸出
Case #1: 4
Case #2: 4

emmm,開局就跑了波網路流。。。T了,後面nb隊友過的。事實上我們可以將這些關係畫出來,我們會發現,對於一個關係組,如果裡面有環的話那麼能夠取得的數字就是整個圖的大小,否則的話就只能取其大小-1個。至於判斷有環無環,我們直接判斷每次出現的兩個數字是否有共同的祖先就可以了,然後我們可以傳遞這種關係。不過由於\(a_i,b_i\)

比較大,所以我們要離散化一下,各位應該寫得比較熟練了,先sort一下再unique一下,接著二分一下就完事了。不過注意的是father等並查集需要用到的陣列也需要開到2倍空間大小。。。。我就因為這個卡了好久QAQ。。。

以下是AC程式碼:

#include <bits/stdc++.h>
using namespace std;

const int mac=1e5+10;
const int inf=1e9+10;

int au[mac],av[mac];
int num[mac<<1],father[mac<<1],bk[mac<<1];
int sz[mac<<1],lu[mac],lv[mac];

int find(int x){return x==father[x]?x:father[x]=find(father[x]);}

int main()
{
    int t;
    scanf ("%d",&t);
    for (int cse=1; cse<=t; ++cse){
        int n,cnt=0;
        scanf ("%d",&n);
        for (int i=1; i<=n; i++){
            scanf ("%d%d",&au[i],&av[i]);
            num[++cnt]=au[i],num[++cnt]=av[i];
        }
        sort(num+1,num+1+cnt);
        int it=unique(num+1,num+1+cnt)-num;
        for (int i=1; i<=n; i++){
            lu[i]=lower_bound(num+1,num+it,au[i])-num;
            lv[i]=lower_bound(num+1,num+it,av[i])-num;
        }
        int ans=0;
        for (int i=1; i<it; i++) father[i]=i,bk[i]=0,sz[i]=1;
        for (int i=1; i<=n; i++){
        	int u=lu[i],v=lv[i];
        	int fu=find(u),fv=find(v);
        	if (fu!=fv){
        		father[fu]=fv;
        		sz[fv]+=sz[fu];
        		bk[fv]=max(bk[fv],bk[fu]);
        	}
        	else {
        		bk[fv]=1;
        	}
        }
        for (int i=1; i<it; i++){
        	if (father[i]!=i) continue;
        	ans+=sz[i]-1;
        	if (bk[i]) ans++;
        }
        printf ("Case #%d: %d\n",cse,ans);
    }
    return 0;
}