带权并查集

今天被队友鄙视了一下,决心复习

1、POJ 食物链

http://poj.org/problem?id=1182 “传送门”

思路:

A吃B , A->B 就让 relation[A] = relation[B] + 1, 然后模3 。 这么做当 B->C C->A的时候不会有问题吗? 不会, 仔细分析 并查集的结构是这样的
假设给出 A1->A , B1->A , C1->B1, D1->C1,

A
//A1// B1///C1///D1

并查集到最后变成所有的parent[ ] = A, 区分不同层次是用relation[],并查集的权值来区分。
还是上面的例子 A1->A , B1->A , C1->B1, D1->C1 则得到relation[]为
1 / 1 // 2 / 0(2+1 %3)这样的话就可以通过权值分析出关系了

#include <iostream>
#include <cstdio>
#include <cstdlib>

using namespace std;
const int maxn = 50050;

int parent[maxn], relation[maxn];

void init(int len)
{
    for(int i=0; i<=len; i++)
    {
        parent[i] = i;
        relation[i] = 0;
    }
}

int root(int x)
{
    if(parent[x] == x)
        return x;
    int temp = parent[x]; // 操作之前的祖先
    parent[x] = root(parent[x]); // 操作之后 真正祖先
    relation[x] = (relation[x] + relation[temp])%3; // x - >操作前祖先  + 操作前祖先->操作后祖先

    return parent[x];
}
//    a   b   root(a) , root(b)  , distance
void merg(int a, int b, int x,int y, int d)
{

    // a + d -- b
    // 初始化  意思是a 为 根
    // 权值就是当前点到 根的距离, 那么根的权值 小

    int cnta = (d + relation[a] - relation[b] + 3) % 3;
    parent[y] = x;
    relation[y] = cnta;
}

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    init(n);
    int ans = 0;
    while(m--)
    {
        int d,a,b;
        scanf("%d%d%d",&d,&a,&b);
        if(a>n || b>n || (d == 2&&a == b))
        {
            ans++;
            continue;
        }
        if(d == 1)
        {
            int x = root(a), y = root(b);
            if(x == y)
            {
                if(relation[a] != relation[b])
                    ans++;
            }
            else // 两只同类,如果之前未确立关系,那么应该要确立关系 而不是归为说假话
            {
                merg(a,b,x,y,0);
            }
        }
        else
        {
            int x = root(a), y = root(b);
            if(x == y) // 一棵树上的
            {
                // 两种类型的关系都不符合
                if(relation[a] != relation[b] + 1 && relation[b] != relation[a] + 2)
                    ans++;
            }
            else
                merg(b,a,y,x,1);
        }
    }
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值