poj 1182 食物链

并!查!集!好!难!啊!

看了两天才看懂我对自己的智商完全没自信了╭(╯^╰)╮

所谓的种类并查集。。一个集合代表一类,求解各类之间的关系问题。

对于本题,

首先,集合里的每个点我们都记录它与它这个集合(或者称为子树)的根结点的相对关系rank。0表示它与根结点为同类,1表示它吃根结点,2表示它被根结点吃。

0,1,2也是距离根节点的具体,rank这个树最多三层,每一层是同类的集合。

题目要判断输入的关系是否合法,可以发现,

1、若两点都没出现过,则合法,肯定啊

2、若出现过某一点a,则若d=1,添加b到a的同类集合,若d=2,则添加b到a的对应关系位置集合

也就是,若有点是第一次出现,一定是合法的,直接进行merge。

3、当两点都出现过,则要判断他们的关系是否合法,利用我们的rank关系就可以知道

若d=1,则a b必须在同一位置,即rank相等时,合法。

若d=2,则a-b=1或-2时合法,

总之,可以用不知道哪个大牛第一个推出来的公式,

按(rank[a]-rank[b]+3)%3!=d-1 判断,一步到位。


以上是总体思路。对于如何merge,更新rank又是个问题

合并时,由于rank这个就三层,0,1,2,0是根,跟上面判断合法是一个道理,也可以用上面那个公式得到。

更新rank啊这个我就更想不到了,直接在root里面更新自己到根的rank,比较好理解。



#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#define inf 0x3f3f3f3f
#define ll __int64
using namespace std;

int r[50005],n,k,d,ans,rank[50005];

int root(int a)
{
    if(r[a]==a) return a;
    int tmp=r[a];
    r[a]=root(r[a]);
    rank[a]=(rank[a]+rank[tmp])%3;
    return r[a];
}

void merge(int a,int b)
{
    int ra,rb;
    ra=root(a);
    rb=root(b);
    r[ra]=rb;
    rank[ra]=(rank[b]-rank[a]+2+d)%3;
}

int main()
{
    int i,a,b,ra,rb;
    scanf("%d%d",&n,&k);
    ans=0;
    for(i=1;i<=n;i++)
        r[i]=i;
    memset(rank,0,sizeof rank);

    while(k--)
    {
        scanf("%d%d%d",&d,&a,&b);
        if(a>n||b>n||(a==b&&d==2))
        {
            ans++;
            continue;
        }
        ra=root(a);
        rb=root(b);
        if(ra==rb)
        {
            if((rank[a]-rank[b]+3)%3!=d-1)
                ans++;
        }
        else
            merge(a,b);
    }
    printf("%d\n",ans);
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值