poj 1182 並查集經典題…


這是並查集非常經典的題目——帶權並查集 。

下面就通過分析這個題目 , 來講講帶權並查集:

這題中 , 對於動物之間有三種關系 , 同類、吃、被吃 , 我這么來存儲這些不同的狀態

0 : a 、b是同類
1 : a 吃 b
2 : a 被 b吃

那么對於兩個動物之間的關系 , 我們只需要判斷他們之間的值就行 , 但是如果只用這種方法 , 必然會TEL

我們這么來考慮這個題目:假如我們知道a、c和b、c之間的關系 , 那么我們就能知道a、b之間的關系
所以 , 在並查集中 , 我們只需要a、b分別和根結點的關系 , 我們就能知道a、b之間的關系
這樣 , 我們在查找時 , 就只需要在O(1)的時間之內 ——這就是帶權並查集

類別偏移:
    如果我們在並查集中 , 要把兩個不同樹合並成一個樹時 , 我們同時也要改變每個點相對於根結點的關系 , 因為根節點已經改變了 , 這就是類別偏移 , 所以每個點和根節點的關系 , 要隨着根節點的改變而改變。怎么改變呢?這里我們用到了向量的思想:
  這是一個向量圖 : c = a + b;
通過向量方程 , 我們就能得到類別偏移方程:f[x] = (f[fa] + f[x])%3 , fa表示x的原根節點 , f[fa]表示節點fa相對於當前根節點的關系 , f[x]表示x相對於原根節點fa的關系 , 所以x相對於當前根節點的關系就是f[fa] + f[x];

所以帶權並查集的關鍵在於找到節點相對於根節點的關系 , 根節點相當於就是一個中間變量 , 這樣每個點妒能有一個確定的相對值。

這個題目很有問題 , 不能用while進行多組測試用例的運行 , 只能一組一組運行 , 不然就會w
代碼:
#include
#include
#include
using namespace std;

int n , k;
int p[50010] , f[50010];

int find(int x)
{
    if(p[x] == x)  return x;
    int fa = p[x];
    p[x] = find(fa);
    f[x] = (f[fa] + f[x])%3;
    return p[x];
}

int main()
{
   
        scanf("%d %d" , &n , &k) ;
        int i ;
        int x , y , z , g , h;
        for(i = 1; i <= n; i++)
            p[i] = i , f[i] = 0;
        //p記錄兩種動物是否有關系 , f記錄兩種動物是什么關系
       
        int sum = 0;
        for(i = 1; i <= k; i++)
        {
            scanf("%d %d %d" , &z , &x , &y);
            if( x > n || y > n)
            {
                sum += 1;
                continue;
            }
            if(x == y && z == 2) 
            {
                sum += 1;
                continue;
            }
            g = find(x) , h = find(y);
            if(g == h && (f[y] + z + 2) % 3 != f[x])
            {
                sum += 1;
               
      

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com