图——并查集——带权并查集

》》》

一、 带权并查集的found 的函数:

found函数找父亲的同时,可以维护关系

int found(int x)
{
    if(par[x] == x)return par[x];
    else 
    {
        int t;
        t = par[x];
        par[x] = found(par[x]);
        
        value[x] += value[t];
        return par[x];
    }
}
二、带权并查集的难点(合并)

对于带权并查集难就难在 合并时 关系的处理。
如果x,y不在一个集合, 现在 知道了x ,y 的关系。 然后要合并x ,y所在的集合。
①先合并:就是rootx 指向y。
②然后处理 rootx与y的关系:
知道rootx与y的关系 == rootx与 x 的关系 + x与y的关系。
因此要通过x与rootx的关系 求出逆关系, 即rootx与x 的关系。

相关题:

最简单的, 模板题
HihoCoder - 1515

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn = 1e5+50;
int par[maxn];
int r[maxn];

void init(int N)
{
    memset(r, 0, sizeof(r));
    for(int i = 1;i <= N;i++)
    {
        par[i] = i;
    }
}
int found(int x)
{
    if(par[x] == x)return par[x];
    else
    {
        int t = par[x];
        par[x] = found(par[x]);
        r[x] += r[t];
        return par[x];
    }

}

void Union (int x, int y, int s)
{
    int rootx = found(x);
    int rooty = found(y);

    if(rooty!= rootx)
    {
        par[rootx] = y;
        r[rootx] = (s - r[x]);/**将 x 与 y的关系 转换为 rootx  与 y 的 关系*/


    }

}

void input(int N, int M)
{
    init(N);

    for(int i = 1;i <= M;i++)
    {
        int x, y, s;
        scanf("%d %d %d", &x, &y, &s);
        Union(x, y, s);
    }

}

void query(int Q)
{
    while(Q--)
    {
        int x, y;
        scanf("%d %d", &x, &y);
        if(found(x)!= found(y))
        {
            printf("-1\n");
        }
        else
        {
            printf("%d\n", r[x] - r[y]);
        }
    }


}

int main()
{
    int N, M, Q;
    scanf("%d %d %d", &N, &M, &Q);
    input(N, M);

    query(Q);



    return 0;
}

poj 1812 食物链
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn = 5e4+500;
int par[maxn];// 并查集 父亲数组
int r[maxn];//权值 表示食物链之间的关系

void init(int N)
{
    memset(r, 0, sizeof(r));
    for(int i = 1;i <= N;i++)
    {
        par[i] = i;
    }

}

int found(int x)
{
    if(par[x] == x)return par[x];
    else
    {
        int t = par[x];
        par[x] = found(par[x]);

        r[x] = (r[x]+r[t])%3;
        return par[x];

    }


}
void Union(int x, int y, int rel)
{
    int rootx = found(x);
    int rooty = found(y);

    par[rooty] = rootx;
    if(rel == 1)rel = 0;// rel 等于 1  y 对 x的 关系 为 0
    else rel = 1;/****rel  等于 2    x吃y   y对x 就是 1(y被 x吃)***/
    r[rooty] = (r[x] + rel + (3-r[y]) )%3;/****rooty 跟根的关系就是 x 根rootx 的关系 + y根x的关系 + rooty 跟y的 关系 mod 3*******/

    
}

int main()
{
    int N, K, cnt = 0;
    scanf("%d %d", &N, &K);

    init(N);

    while(K--)
    {
        int rel, x, y;
        scanf("%d %d %d", &rel, &x, &y);

        if(x > N || y > N)cnt++;
        else if(rel == 2 && x == y)cnt++;
        else if(found(x) == found(y))
        {
            if(rel == 1 && r[x]!= r[y])cnt++;
            else if(rel == 2 &&r[x]!= (r[y]+2)%3)cnt++;
        }
        else
        {
            Union(x, y, rel);
        }

    }
    printf("%d",cnt);


    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值