Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 31929 | Accepted: 9326 |
Description
現有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
以下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
分析:經典並查集
1.p[x]表示x根結點。r[x]表示p[x]與x關係。r[x]=0 表示p[x]與x同類;1表示p[x]吃x;2表示x吃p[x]。
2.怎樣劃分一個集合呢?
注意,這裏不是根據x與p[x]是否是同類來劃分。而是根據“x與p[x]能否確定兩者之間關係”來劃分,若能確定x與p[x]關係,則它們同屬一個集合
3.怎樣判斷一句話是不是假話?
假設已讀入D ,X ,Y ,先利用findset()函數得到X,Y所在集合代表元素fx,fy,若它們在同一集合(即fx==fy)則可以判斷這句話真僞:
若 D==1 而 r[X]!=r[Y] 則此話爲假.(D==1 表示X與Y爲同類,而從r[X]!=r[Y]可以推出 X 與 Y 不同類.矛盾.)
若 D==2 而 r[X]==r[Y](X與Y爲同類)或者r[X]==(r[Y]+1)%3(Y吃X)則此話爲假。
4.上個問題中r[X]==(r[Y]+1)%3這個式子怎樣推來?
假設有Y吃X,那麼r[X]和r[Y]值是怎樣?
我們來列舉一下:
r[X]=0&&r[Y]=2
r[X]=1&&r[Y]=0
r[X]=2&&r[Y]=1
稍微觀察一下就知道r[X]=(r[Y]+1)%3;
事實上,對於上個問題有更一般判斷方法:
若(r[Y]-r[X]+3)%3!=D-1 ,則此話爲假.
5.其他注意事項:
在Union(d,x,y)過程中若將S(fy)合併到S(fx)上,則相應r[fy]必須更新爲fy相對於fx關係。怎樣得到更新關係式?
r[fy]=(r[x]-r[y]+d+3)%3;
- #include<cstdio>
- const int N=50001;
- int p[N],r[N],n;
- int findset(int x)
- {
- if(x!=p[x])
- {
- int fx=findset(p[x]);
- r[x]=(r[x]+r[p[x]])%3;
- p[x]=fx;
- }
- return p[x];
- }
- bool Union(int d,int x,int y)
- {
- int fx=findset(x),fy=findset(y);
- if(fx==fy)
- {
- if((r[y]-r[x]+3)%3!=d)return 1;
- else return 0;
- }
- p[fy]=fx;
- r[fy]=(r[x]-r[y]+d+3)%3;
- return 0;
- }
- int main()
- {
- int k,ans,i,d,x,y;
- scanf("%d%d",&n,&k);
- ans=0;
- for(i=1;i<=n;i++)p[i]=i,r[i]=0;
- while(k--)
- {
- scanf("%d%d%d",&d,&x,&y);
- if(x>n||y>n||(x==y&&d==2)){ans++;continue;}
- if(Union(d-1,x,y))ans++;
- }
- printf("%d\n",ans);
- return 0;
- }