带权并查集 poj1182

/*带权并查集:
                  1 所谓的带权,我的理解是根节点和儿子节点之间的关系不再是那种单纯的所属关系,而是增加了一个权值来表示他们之间的特殊关系。比如距离就可以作为权值,在带权并查集里,需要注意的是。首先,rank数组不再表示哪一个是大树,而是表示某个节点到根节点的权值。第二在路径压缩的过程中需要利用递归的思想把路径上的点的rank值进行更新。最后就是合并的时候,不再是按照rank大的,而是统一一个方向合并,比如右边统一合并到左边。
                   2  经常利用权值为0表示两个元素的关系是相同类型,权值为1表示两个元素关系是不同的类型。所以要用到mod 2等(其它还有mod 3的情况等等)
                   3 注意只要涉及到两个rank相减的时候,都有可能变成负数,比如要加上一个数mod 2时候加2再mod 2....
                                4 带权并查集要注意,最后应该使得所有的路径都要压缩到根节点上面
*/
/*本题用rank[x]记录x与x的最远的祖先的关系。 这里定义rank[x]=0表示x与x的祖先是同类。rank[x]==1表示x吃x的祖先。rank[x]==2表示x的祖先吃x;这样定义后就与题目中输入数据的D联系起来,(D-1)就可以表示x与y的关系。这样就可以用向量的形式去推关系的公式了。我们用f(x,father[x])表示rank[x]的值;*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
int fa[1000] = {0};
int rank[1000] = {0}; 
int n;

void Initial()
{
    for(int i = 1; i <= n; i++)
    {
        fa[i] = i;
        rank[i] = 0;
    }
}

int getfather(int x)
{
    if(x == fa[x])
        return x;
    int oldfa = fa[x];
    fa[x] = getfather(fa[x]);
    rank[x] = (rank[x] + rank[oldfa]) % 3;//将他们的关系更新//用向量的形式很快就可以看出来
    return fa[x];
}

void unionset(int r, int x, int y)
 {
    int fx = getfather(x);
    int fy = getfather(y);
    if(fx == fy)            //已经是同一个祖先说明在环关系(不一定是完整的环)内,但那个不一定是真正的祖先
        return ;            //例如:测试数据:2 1 2后1的father是2,2的father是2,祖先暂时是2
    else                    //不在同一个环,则合并,使他们在同一个环关系
    {
        fa[fx] = fy; 
        rank[fx] = (rank[y] + r - rank[x] + 3) % 3; //这里同样可以用向量来推公式。另外需要注
        //意的是,这里只更新了fx的rank值,而fx的儿子的rank值都没有更新会不会有问题。其实不碍事,
        //由于我们每次输入一组数据我们都对x和y进行了getfather的操作(x>n || y>n ……)的除外。在执
        //行getfather的操作时,在回溯的过程中就会把fx的儿子的rank值都更新了。
    }
    return ;
}

int istrue(int d, int x, int y)
{
    int fx, fy;
    if(x>n || y>n || ((x==y)&&(d==2)))
        return 0;
    fx = getfather(x);
    fy = getfather(y);
    if(fx != fy)        //不在同一个环中,所以调到main()函数然后到unionset函数中合并关系
        return 1;
    else                //如果在一个环中就检查他们两个的关系
    {
        if(rank[x] == ((d - 1) + rank[y])%3)//关系正确//其实x与Y的关系已经通过前面的公式//推出,现判断输入的关系是否正确
            return 1;// 这个公式可以用向量来推:如果
            //( f(x,y) + f(y,father[y]))% 3 == f(x,father[x]) 则是正确的,否则是错的。
            //这个形式可以用向量来表示,就是判断这个向量加法对不对   x--->y + y---> fx(fy)
            //是否等于  x--->fx(fy)
        else                                //关系错误
            return 0;
    }
}

int main()
{
    int k, i, x, y, d;
    int ans = 0;
    scanf("%d%d", &n, &k);
    Initial();
    for(i = 1; i <= k; i++)
    {
        scanf("%d%d%d", &d, &x, &y);
        if(!istrue(d, x, y))
            ans++;
        else
            unionset(d - 1, x, y);
    }
    printf("%d\n", ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值