NOI 2001 食物链 题解

这个比我还年长的题用所谓的带权并查集。

首先,在并查集入门题《亲戚》中我们用并查集描述两人的关系。

所以,我们也可以用并查集来表示动物间食物链的关系。

但是吃与被吃这种关系一个数组是开不完的。

因此,我们得把并查集这个森林中的每颗树的树根利用起来。


若x,y是同类,所以我们从x到y连一条权值为0的有向边。(其实并查集只能连有向边)

若x吃y,那么我们就从x到y连一条权值为1的有向边。

这个假设方法十分方便,因为你可以直接从x到y连一条权值为(d-1)的有向边。

但实际上并不是这样的。


因为两个并查集的合并是将一棵树的根连在另一个根上(废话)。

如果x,y不在同一颗树中:

我们该从x的根向y的根连权值为n的边;

其次,我要说,所有的树边的权值都不用存,存儿子到根的长度dis[  i  ],然后在并查集路径压缩(自行百度)时更改就行。

因为x与y的关系是(d-1),x与y的距离是(d-1)

x树连在y树后dis[x]应该为dis[x]+n.

所以(dis[x]-dis[y]+n)== d-1 

等等,因为关系只有三种:吃,被吃,同类。

所以所有的值都可以(必须)%3(注意负数要处理成正数),毕竟与根距离为0的 和 与根距离为3的动物是同类。。

所以你可以算出n。


然而在我们津津有味的加边,路径压缩后,我们会发现:如果x,y已经在一个集中,也就是意味着我们不可以在他们之间加边,那就判断这句话对不对就行了。。。。。。如果想到这,就很简单了。


AC代码:

#include<cstdio>
#include<cctype>
using namespace std;
int n,k,x,y,d,_x,_y,f[50001],dis[50001],ans;
int find(int x){
    if(x==f[x]) return x;
    int tmp=f[x];
    f[x]=find(f[x]);
    dis[x]=(dis[x]+dis[tmp])%3;
    return f[x];
}
void getint(int &t){
    char c;
    t=0;
    do{c=getchar();}while(!isdigit(c));
    while(isdigit(c)){
        t=t*10+c-'0';c=getchar();
    }
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) f[i]=i;
    for(int i=1;i<=k;i++){
        getint(d);getint(x);getint(y);
        if(x>n||y>n){ans++;continue;}
        _x=find(x);
        _y=find(y);
        if(_x!=_y){
            f[_x]=_y;
            dis[_x]=(dis[y]-dis[x]+d-1+3)%3;
        }
        else
            if(((dis[x]-dis[y])%3+3)%3 != d-1)
                ans++;
    }
    printf("%d",ans);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值