1. 程式人生 > >poj 1182 (帶權並查集)

poj 1182 (帶權並查集)

ios int 查找 食物 spa script ble 距離 輸出

食物鏈
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 71361 Accepted: 21131

Description

動物王國中有三類動物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
欸困惑許久的一道題,寒假學並查集時已經遇見,卡在帶權並查集一直沒時間or感覺很復雜就沒學,前幾日突然想起,便想起他了.
帶權並查集感覺在於對向量偏移的理解和對getf()函數遞歸查找祖先的深刻理解,怎奈數學砸砸看見數學就頭疼>_<
本題體面給出了三個類,並查集的作用:將具有相同性質的某些點歸到一顆樹裏,他們有共同的祖先.
難不成三個類要用三個並查集?顯然用三個只會更加復雜,我們不妨利用帶權並查集點到祖先的距離的不同來表示點與祖先的關系,將所有的點歸到一顆樹下,假設有兩個點x,y在一顆樹裏且我們知道其到祖先的距離d[x],d[y],
顯然利用這兩個"距離"(關系)我們足以推出x,y的關系,這便涉及到向量了.
帶權並查集主要就是在getf()函數遞歸的過程中順便改變到根節點的距離這一步!合並函數中也需要進行小小的改變.
推論:已知x,x的父親t=f[x],根祖先root和d[x],d[t]; 我們要做的就是根據 d[x],d[t]更新出當前正確的d[x]的值.
窮舉出所有的可能後:{0,1,2}--{0,1,2}不難得出結論 d[x]=(d[x]+d[t])%3;
為了保證 d[t]的值是x的父親到祖先的距離我們需要先遞歸x的父親更新他父親的距離......
如果不先進行遞歸的話,d[t]的值可能並不是t到其根的距離導致結果錯誤!
merg函數類似利用向量推出公式即可,利用0,1,2的巧妙之處在於可以用(d-1)直接表示出x與y的關系.

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int f[50005],d[50005];
int getf(int v)
{
if(f[v]==v) return v;
int t=f[v];
f[v]=getf(f[v]);
d[v]=(d[v]+d[t])%3;
return f[v];
}
void merg(int x,int y,int rx,int ry,int D)
{
f[ry]=rx;
d[ry]=(3-d[y]+(D-1)+d[x])%3;
}
int main()
{
int n,k,x,y,D,ans=0,i,j;
cin>>n>>k;
for(i=1;i<=n;++i) f[i]=i;
memset(d,0,sizeof(d));
while(k--){
scanf("%d%d%d",&D,&x,&y);
if(x>n||y>n||(x==y&&D==2)) {++ans;continue;}
int rx=getf(x),ry=getf(y);
if(rx==ry){
if((D-1)!=(3-d[x]+d[y])%3) ++ans;
}
else merg(x,y,rx,ry,D);
}
cout<<ans<<endl;
return 0;
}

poj 1182 (帶權並查集)