食物链(并查集)

思路:

(1)细数本题操作:

  1. 对于输入x,y :若未建立联系,则建立关系网;若已经建立联系,则判断联系是否一致。
  2. 则分为:①判定是否建立联系;②建立联系;③查询联系;三种操作。

(2)注意到输入节点联系可画成树的形式,且相互联系可以转化为与根节点之间的距离进行描述。

(3)于是考虑并查集,通过是否在同一集合内判定是否建立了联系;通过集合合并及距离转换建立联系;通过查询距离查询联系。

(4)距离与联系的关系:用d[x]描述x与其父亲节点之间的距离,当调用find(x)函数进行路径压缩时,x认祖宗为父亲,则d[x]加上原父亲到祖宗之间的距离;由于每次判定都要先找x,y的祖宗,所以一定已经进行了路径压缩,则此时拿到的d[x],d[y]已经是x,y与祖宗之间的距离了;而:

  1. (d[x] - d[y])  %3 == 0 则x,y同类;
  2. (d[x] - d[y])  %3 == 1则x吃y;
  3. (d[x] - d[y])  %3 == 2则y吃x;

通过以上关系:

①判定是否建立联系: px == py;

②建立联系:

在p[px] = py合并后,

  1. 同类:需满足(d[x] + d[px] - d[y])%3 == 0; d[x]还未路径压缩,父亲未变,不需要处理,但px父亲已变,所以令d[px] = d[y] - d[x];
  2. x吃y:需满足(d[x] + d[px] - d[y])%3 == 1; d[x]还未路径压缩,父亲未变,不需要处理,但px父亲已变,所以令d[px] = d[y] + 1 - d[x];

③查询联系:

  1. 若t == 1表示判定为同类,(d[x] + d[px] - d[y])%3 == 0表示实际为同类若不一致则为假;
  2. 若t== 2表示判定x吃y,(d[x] + d[px] - d[y])%3 == 1表示实际x吃y,若不一致则为假;

代码:

#include<iostream>

using namespace std;

const int N = 5e4 + 10;

int p[N],d[N];

int find(int x)
{
    if(x != p[x])
    {
        int t = p[x];
        p[x] = find(p[x]);
        d[x] += d[t];
    }
    
    return p[x];
}

int main()
{
    int n,k;
    cin >> n >> k;
    
    for(int i = 1;i <= n;i ++)
    {
        p[i] = i;
        d[i] = 0;
    }
    
    int res = 0;
    while(k --)
    {
        int t,x,y;
        cin >> t >> x >> y;
        
        int px = find(x),py = find(y);
        if(x > n || y > n)
            res ++;
        else
        {
            
            if(px == py)//查询关系
            {
                if(t == 1)//判断同类
                {
                    if((d[x] - d[y]) %3 != 0)res ++;
                }
                else
                {
                    if((d[x] - d[y] - 1) %3 != 0) res ++;
                }
            }
            else//添加关系
            {
                if(t == 1)
                {
                    p[px] = py;
                    d[px] = d[y] - d[x];
                }
                else
                {
                    p[px] = py;
                    d[px] = d[y] - d[x] + 1;
                    
                }
            }
        }
        
    }
    
    cout << res << endl;
    
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

y_lov

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值