POJ 1182 食物链 带权并查集

来源:互联网 发布:查看windows序列号 编辑:程序博客网 时间:2024/06/12 00:23

食物链

Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 77869 Accepted: 23180

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

Source

Noi 01

分析:
大概是第一次做带权并查集的题,感觉这道题好巧,各种公式什么的理解了好久。

并查集可以查询两个元素是否在同一个元素里,并且很快地合并两个集合。而这道题在并查集简单地合并与查询元素关系的基础上,还加入了元素的权值。(因为这次的关系除了是否同属于一种元素外,还有吃与被吃的关系。)
我们用rk来存储每个元素的关系。其中定义rk的含义如下:
rk==0,表示父子属于同一种元素
rk==1,这个节点被父节点吃
rk==2,父节点被这个节电吃
这样定义之后会有很多方便之处。
直接解释几个公式吧:
rk[g]=(rk[s]+rk[f])%3
这个在路径压缩的时候会用到,即已知一个节点与父节点的关系和父节点和爷爷节点的关系,求这个节点与爷爷节点的关系。
【这个可以自己手动推一下,对每种情况都是满足的

rk[x]+3-rk[y]
这个是在判断x和y是否是同一类的时候用到的。
如果x对y的关系是rk[x],那么y对x的关系就是(3-rk[x])%3 【这个也可以简单模拟一下,不难得出
那么x接rt,关系为rk[x],y也接rt ,关系为rk[y] 我们就可以先得出rt与y的关系,此时x与y就是爷孙关系(感觉这句话好奇怪),然后就可以求出x与y的关系,再与给出的关系判断一下是否一致就可。
注意:如果给出的x和y的关系是0,那么我们只需要判断rk[x]和rk[y]是否一致就可。(不过用这个式子也可以)

rk[fx]=(tmp-1+3-rk[x]+rk[y])%3

tmp为给出的关系。
此时为给出的x和y之前没有在一个集合中(肯定不是谎话),我们需要把x的根fx接在y的根fy上。
具体实现相当于把fx接在x上,x接在y上,y在fy上。
(但要路径压缩)
首先先利用tmp-1得到y与x的关系
再利用3-rk[x] 得到fx对于x的关系
此时就得到了fx与y的关系:
rk[fx]=(tmp-1+3-rk[x])%3;
最后再利用第一个式子得到fx与fy的关系,即给出的式子

马丹理解这些公式理解了好久,但理解完感觉成就感up2333
【好久没写过这么长的博客了

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;inline int read(){    int x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}    return x*f;}const int N = 100010;int ans=0;int n,k;int fa[N],rk[N];int find(int x){    if(x==fa[x]) return x;    int y=find(fa[x]);    rk[x]=(rk[x]+rk[fa[x]])%3;    return fa[x]=y;}int query(int tmp,int x,int y){    int fx=find(x),fy=find(y);    if(fx==fy){        if(tmp==1){            if(rk[x]!=rk[y]) return 1;            return 0;        }        else{            if((rk[x]+3-rk[y])%3!=1) return 1;            return 0;        }    }    fa[fx]=fy;    rk[fx]=(tmp-1+3-rk[x]+rk[y])%3;    return 0;}int main(){    n=read(),k=read();    for(register int i=1;i<=n;i++) fa[i]=i,rk[i]=0;    for(register int i=1;i<=k;i++){        int tmp=read(),x=read(),y=read();        if(x>n||y>n) {++ans;continue;}        if(x==y&&tmp!=1) {++ans;continue;}        ans+=query(tmp,x,y);    }    printf("%d\n",ans);    return 0;}
原创粉丝点击