[Luogu 2024] 食物链

[Luogu 2024] 食物链


几句随感

我依稀记得联赛前本来想做这题的时候。

当年啊弱到题目与标签就令我望而生畏。

还有翻阅很多遍那现在已经被遗弃的博客。

看到题解中「三倍数组」的字眼就怕难而放弃。

如今我来了,晚了太多。

只可惜,总有的步伐是我留不住的。

也许我会说「其实你的写法可以更简单」。

也许我会说「判断条件可以不用那么多的」。

也许我会说「那句 continue 完全可以删除」。

也许,「不是你想 A 就能 A 的」。

也许,「可是该退还是要退的」。

……


题解

以后并查集杜绝 Merge,一律 Union。

建立补集。并查集分三段,每段长为 n,分别表示 A B C。说白了就是 f 数组开三倍,\([1,n],[n+1,2n],[2n+1,3n]\) 分别表示 A B C。

越界肯定是假的,跳过。

如果 x,y 同类,但这俩已经有捕食关系,这句话就是假的。

如果 x 吃 y,但这俩已经是同类或者 y 已经吃 x,这句话就是假的。

否则,这句话就是真的,那么执行下列操作。

x,y 是同类,分别在 A B C 段内 Union(x,y)。

x 吃 y,A->y 连 B->x,B->y 连 C->x,C->y 连 A->x。(当然 x 吃 y 写成 x 连 y 也是可以的,但鉴于本人刚学了生态系统的能量流动,就按生物的标准写了qwq)

最后输出假话数目就好了。

#include <cstdio>
const int MAXN=50010;
int n,k,ans;
class UFS
{
    private:
        int f[MAXN*3];
        int Find(int x)
        {
            return x==f[x] ? x : f[x]=Find(f[x]);
        }
        void Union(int x,int y)
        {
            f[Find(y)]=Find(x);
        }
    public:
        UFS(int n)
        {
            for(int i=1;i<=n*3;++i)
                f[i]=i;
        }
        void UnionSame(int x,int y)
        {
            Union(x,y);
            Union(x+n,y+n);
            Union(x+(n<<1),y+(n<<1));
        }
        void UnionEat(int x,int y)
        {
            Union(x+n,y);
            Union(x+(n<<1),y+n);
            Union(x,y+(n<<1));
        }
        bool Connected(int x,int y)
        {
            return Find(x)==Find(y);
        }
};
int main(int argc,char** argv)
{
    scanf("%d %d",&n,&k);
    static UFS *S=new UFS(n);
    for(int i=1,opt,x,y;i<=k;++i)
    {
        scanf("%d %d %d",&opt,&x,&y);
        if(x>n || y>n)
        {
            ++ans;
            continue;
        }
        if(opt==1)
            if(S->Connected(x,y+n) || S->Connected(x+n,y))
                ++ans;
            else
                S->UnionSame(x,y);
        else
            if(S->Connected(x,y) || S->Connected(x,y+n))
                ++ans;
            else
                S->UnionEat(x,y);
    }
    printf("%d\n",ans);
    delete S;
    return 0;
}

谢谢阅读。

转载于:https://www.cnblogs.com/Capella/p/9131801.html

帮我进行代码调试 题面如下 P2024 [NOI2001] 食物链 题目描述 动物王国中有三类动物 A , B , C A,B,C,这三类动物的食物链构成了有趣的环形。 A A 吃 B B, B B 吃 C C, C C 吃 A A。 现有 N N 个动物,以 1 ∼ N 1∼N 编号。每个动物都是 A , B , C A,B,C 中的一种,但是我们并不知道它到底是哪一种。 有人用两种说法对这 N N 个动物所构成的食物链关系进行描述: 第一种说法是 1 X Y,表示 X X 和 Y Y 是同类。 第二种说法是 2 X Y,表示 X X 吃 Y Y。 此人对 N N 个动物,用上述两种说法,一句接一句地说出 K K 句话,这 K K 句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。 当前的话与前面的某些真的话冲突,就是假话; 当前的话中 X X 或 Y Y 比 N N 大,就是假话; 当前的话表示 X X 吃 X X,就是假话。 你的任务是根据给定的 N N 和 K K 句话,输出假话的总数。 输入格式 第一行两个整数, N , K N,K,表示有 N N 个动物, K K 句话。 第二行开始每行一句话。格式见题目描述与样例。 输出格式 一行,一个整数,表示假话的总数。 输入输出样例 #1 输入 #1 100 7 1 101 1 2 1 2 2 2 3 2 3 3 1 1 3 2 3 1 1 5 5 输出 #1 3 说明/提示 对于全部数据, 1 ≤ N ≤ 5 × 10 4 1≤N≤5×10 4 , 1 ≤ K ≤ 10 5 1≤K≤10 5 。代码如下:#include<bits/stdc++.h> using namespace std; /* 扩展域并查集 分三层去建 第一层是原始的 第二层是我吃他 第三次是他吃我 如果第一层的与第二层的某一个相连,表示第一层吃第二层 如果第一层的与第三层的某一个相连,表示第三层吃第一层 然后如果同一个父亲,就是同类 */ const int MAXN = 5e4 + 5; int N, K; int op, x, y, cnt; int fa[MAXN]; void init( int n ){ for( int i = 1; i <= n; i ++ ) fa[i] = i; } int fd( int x ){ if( fa[x] == x ) return x; else return fa[x] = fd( fa[x] ); } void join( int x, int y ){// y 加入 x int fx = fd( x ); int fy = fd( y ); if( fx != fy ) fa[fx] = fy; return; } int main(){ cin >> N >> K; init( N ); while( K – ){ cin >> op >> x >> y; if( x > N || x <= 0 || y > N || y <= 0 ){ cnt ++; continue; } if( op == 1 ){ join( x, y ); join( x + N, y + N ); join( x + 2 * N, y + 2 * N ); if( fd( x ) == fd( y + N ) || fd( y ) == fd( x + N ) ){//有捕食关系 cnt ++; // cout << x << " " << y << endl; } } else{ join( x, y + N ); join( x + N, y + 2 * N ); // join( y + 2 * N, x ); join( x + 2 * N, y + N ); if( fd( x ) == fd( y ) || fd( x ) == fd( y + 2 * N ) ){ cnt ++; // cout << x << " " << y << endl; } } } cout << cnt; return 0; }
09-07
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值