poj 1182 食物链

http://poj.org/problem?id=1182

并查集的灵活应用 

代码:

/*

可以这样理解

并查集是由很多树组成的,这些树不断的合并

下面代码f[]仍然代表此节点所属的树根

而d[]表示此点到父节点的差值,但是每次求fx()(树根)时,节点都更新指向树根

假如比父节点大1 则此点可以吃掉父节点

假如比父节点大2 则此点可以被父节点吃掉

假如和父节点相等,则属于同类

这里所指的大是针对当前节点对父节点而言的,2比1大1   0比2大1(循环)

由于更新完都指向树根了,所以就有了相同的参照物

根据不同的点和树根的关系,可以推算出一棵树任意两点之间的关系



问题就在于如何维护树的合并,f[]还是按照原来的方法,d[]需要根据实际情况取余

理解是注意自己画图

-----见代码注释



*/

#include<cstdio>



using namespace std;

const int N=50005;

int f[N],d[N];

int fx(int x)

{

    if(f[x]!=x)

    {

        int tmp=f[x];

        f[x]=fx(f[x]);

        //更新到这来时,x的父节点已经更新完成 指向根节点

        //而且x的父节点和根节点直接的关系也更新完成

        //结下了要更新x节点 因为x节点已经也指向了根节点(通过给f[x]赋值)

        //所以要根据父节点tmp到根节点的关系和x节点到父节点的关系

        //推算出x节点到根节点的关系,下面的式子就是计算过程 注意对3取余

        d[x]=(d[x]+d[tmp])%3;

    }

    return f[x];

}

int main()

{

    //freopen("data.in","r",stdin);

    int n,m;

    scanf("%d %d",&n,&m);

    for(int i=1;i<=n;++i)

    {f[i]=i;d[i]=0;}//初始化d[i]=0 因为每个节点开始指向自己 和自己是同类

    int ans=0;

    while(m--)

    {

        int a,b,w,A,B;

        scanf("%d %d %d",&w,&a,&b);

        if(a>n||b>n||a<=0||b<=0)

        {++ans;continue;}

        A=fx(a);

        B=fx(b);

        //这时候通过 fx(a),fx(b) a和b都更新完成,d[a]和d[b]都表示和各自根节点(可能相同,可能不同)关系

        if(w==1)

        {

            if(A==B)//假如 a和b 属于同一颗树

            {

                if(d[a]!=d[b])//如果不是同一类,矛盾

                ++ans;

            }else//假如不是同一颗树

            {

                f[A]=B;//合并两棵树

                //必须保证合并后d[a]和d[b]和树根B的关系一样

                //那么就得满足d[a]+d[A]==d[b](%3) 所以。。。

                d[A]=(d[b]+3-d[a])%3;

            }

        }else

        {

            if(A==B)//假如 a和b 属于同一颗树

            {

                if((d[b]+1)%3!=d[a])//则d[a] 比 d[b] 大1 否则矛盾

                ++ans;

            }else//假如不是同一颗树

            {

                f[A]=B;//合并

                //同样的原理 只不过要多加1 因为a吃b

                d[A]=(d[b]+4-d[a])%3;

            }

        }



    }

    printf("%d\n",ans);

    return 0;

}

  

 

你可能感兴趣的:(poj)