POJ1182-並查集

這題有一個關鍵點: x的食物的食物以x爲食,即生物間的關係是以3爲循環的,就像運算 (0+1)%3=1,(1+1)%3=2,(2+1)%3=0,(0+1)%3=1... ...

不管d=1還是d=2,都表示x與y有關係,因此可以併到一個並查集裏去,然而具體的同類與捕食關係可用0,1,2來代表;

這裏以r[i]表示i與其並查集中父節點p[i]的關係: 0表示同類,1表示i吃p[i],2表是i被p[i]吃;

那麼如果x和y在同一個並查集時,可通過查詢x與根節點的關係,y與根節點的關係來判斷x與y的關係。若在並查集的find操作中壓縮路徑的話,根節點也就成了父節點,好方便!

另外合併和查找時可以利用一些加法,對3取模的運算來更新r。

AC代碼:

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

const int NN=50005;

int n;
int p[NN]; //根節點
int r[NN]; //與父節點的關係,0:同類,1:吃根節點,2:被根節點吃

inline void get(int &x)
{
    char c=getchar();
    while (c<'0' || c>'9') c=getchar();
    x=c-'0';
    c=getchar();
    while (c>='0' && c<='9') x=x*10+c-'0',c=getchar();
}

int find(int x)
{
    if (p[x]!=x)
    {
        int t=p[x];
        p[x]=find(p[x]);
        r[x]=(r[x]+r[t])%3; //x與新父節點(根節點)的關係
    }
    return p[x];
}

int Union(int d,int x,int y)
{
    int fx=find(x);
    int fy=find(y);
    p[fx]=fy;
    r[fx]=(r[y]-r[x]+2+d)%3; //fx與fy的關係=y與fy的關係和x與fx的關係差+x與y的關係
}

int main()
{
    int d,x,y,k,cnt=0;
    scanf("%d%d",&n,&k);
    for (int i=1; i<=n; i++)
    {
        p[i]=i;
        r[i]=0;
    }
    while (k--)
    {
        get(d); get(x); get(y);
        if (x>n || y>n) cnt++;
        else if (d==2 && x==y) cnt++;
        else if (find(x)!=find(y)) Union(d,x,y);
        else if ((r[y]+d+2)%3!=r[x]) cnt++;//這句話一定是在find(x)和find(y)之後的,因爲find過後r[x]纔是x與其根節點的關係
    }
    printf("%d\n",cnt);
    return 0;
}


發佈了100 篇原創文章 · 獲贊 2 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章