【带权并查集】POJ1182 [NOI2001]食物链

来源:互联网 发布:软件的版权声明 编辑:程序博客网 时间:2024/04/28 10:33

题面在这里

这是一道经典的带权并查集例题。
我们定义权值s[i]表示节点i与根的关系:
0:和根是同一种动物
1:i能够吃根节点
2:i被根节点吃
这样定义的好处就是,在模3的意义下,各个节点之间的关系能够传递
(接下来本题解所讨论的范围都是在模3的意义下)
例如:
这里写图片描述
这样我们的权值s[i]就能满足一维向量加法了。

那么很显然,s[i]也满足一维向量减法:
这里写图片描述
a与b的关系就是s[a]-s[b]

再来考虑如何解此题:
若给出的X,Y未确定关系(X,Y拥有不同的根)显然这句话是对的,
然后把X,Y所在集合合并。
这里讲一下合并时权值如何修改:
设当前语句的条件是t(即x与y的关系),x、y的根分别是fx,fy
这里写图片描述
有:s[x]+?=t+s[y]
即:?=t+s[y]-s[x]
那么合并就是这样的:
fa[fx]=fy s[fx]=t+s[y]-s[x]

剩下的情况就是不用修改的,注意这时X,Y已经有公共祖先了
可以得到X,Y的关系是s[x]-s[y]
只需判断s[x]-s[y]是否与给定关系t相等即可

代码:

#include<cstdio>#include<cstring>const int maxn=50005;int n,q,fa[maxn],s[maxn],ans=0;inline int red(){    int tot=0;char ch=getchar();    while (ch<'0'||'9'<ch) ch=getchar();    while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();    return tot;}int getfa(int x){    if (fa[x]==x) return x;    int father=fa[x];    fa[x]=getfa(fa[x]);    s[x]=(s[x]+s[father])%3;    return fa[x];}int main(){    n=red(),q=red();    for (int i=1;i<=n;i++) fa[i]=i;    while (q--){        int ch=red(),x=red(),y=red();        if (x>n||y>n) {ans++;continue;}        if (ch==1){            int fx=getfa(x),fy=getfa(y);            if (fx!=fy) fa[fx]=fy,s[fx]=(s[y]-s[x]+3)%3;else ans+=(s[x]-s[y]+3)%3!=0;        }else{            int fx=getfa(x),fy=getfa(y);            if (fx!=fy) fa[fx]=fy,s[fx]=(s[y]-s[x]+4)%3;else ans+=(s[x]-s[y]+3)%3!=1;        }    }    printf("%d",ans);    return 0;}
3 0
原创粉丝点击