Acwing 240. 食物链

该程序使用并查集数据结构处理环状食物链问题,通过维护点到根节点的距离来判断不同点之间的吃与被吃关系。当接收到新的食物链信息时,检查是否符合模3关系,并进行集合的合并与路径压缩。最后输出不符合规则的谎话数量。
//核心思想:并查集维护点到根节点的距离
//由于是环状食物链,所以可构建以下关系
//通过集合里各点到根节点的距离来判断各点间的关系
//1. 点到根节点距离模3为1 该类点吃3.情况的点,被2.情况的点吃
//2. 点到根节点距离模3为2 该类点吃1.情况的点,被3.情况的点吃
//3. 点到根节点距离模3为0 该类点吃2.情况的点,被1.情况的点吃
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
const int N = 5e4+10;
int res = 0;
int p[N],d[N];//d[i]表示i点到根节点的距离
int find(int x)  // 并查集维护各点到根节点的距离
{
    if (p[x] != x)
    {
        int u = find(p[x]);//这里需要先存一下根节点
        d[x]+=d[p[x]];//让d[x]先加上p[x]到根节点的距离
        p[x]=u;//再路径压缩,更新p[x]
    }
    return p[x];
}
int main()
{
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++) p[i]=i;
    for(int i=0;i<k;i++)
    {
        int a,b,c;
        cin>>c>>a>>b;
        int pa=find(a),pb=find(b);
        if(a>n||b>n)
        {
            res++;
        }
        else if(c==1)
        {
            if(pa==pb&&(d[a]-d[b])%3) res++;//如果二者已在集合内,且不为同类,则为谎话,res++
            else if(pa!=pb)//如果二者还未在集合内,则合并集合
            {
                p[pa]=pb;
                //d[pa]需要我们自己构造,使得a,b两点满足同类关系
                d[pa]=(d[b]-d[a]);//因为二者为同类,所以到根节点的关系要满足(d[a]+d[pa])%3==d[b]%3

            }
        }
        else if(c==2)
        {
            if(pa==pb&&(d[a]-d[b]-1)%3) res++;//如果二者已在集合内,且不为a吃b的关系,则为谎话,res++
            else if(pa!=pb)//如果二者还未在集合内,则合并集合
            {
                p[pa]=pb;
                //同上,d[pa]需要我们自己构造,使得a,b两点满足a吃b的关系
                d[pa]=(d[b]+1-d[a]);//因为二者存在a吃b的关系,所以到根节点的关系要满足(d[a]+d[pa]-1)%3=d[b]%3
            }
        }
    }
    cout<<res;
}

### 并查集的概念及其在AcWing中的实现 #### 什么是并查集? 并查集是一种树型数据结构,用于处理一些不相交集合(Disjoint Sets)的合并与查询问题。它支持两种主要的操作:`Find` 和 `Union`。通过这两种操作,可以在几乎常数的时间复杂度内完成集合的查找和合并。 #### 并查集的核心功能 1. **查找** (`Find`):确定某个元素属于哪个子集,这可以通过找到该元素所在子集的代表来实现。 2. **合并** (`Union`):将两个不同的子集合并成一个新的子集。 这些基本操作使得并查集成为解决动态连通性问题的理想工具[^1]。 #### AcWing平台上关于并查集的内容 AcWing 提供了许多有关并查集的学习资源和题目练习机会。例如,在 `[AcWing] 240. 食物链` 中,展示了如何利用并查集的思想去解决问题。此题不仅涉及到了基础的并查集概念,还扩展了其应用范围至更复杂的场景——食物链关系建模。 以下是基于 C++ 的标准并查集模板代码: ```cpp #include <iostream> using namespace std; const int MAXN = 1e5 + 5; int parent[MAXN]; // 存储每个节点的父亲节点 int size_set[MAXN]; // 记录每棵树所含有的节点数量 // 初始化函数 void init(int n){ for (int i=0;i<=n;i++) { parent[i]=i; size_set[i]=1; } } // 查找函数 带路径压缩优化 int find_root(int x){ return parent[x]==x ? x : parent[x]=find_root(parent[x]); } // 合并函数 按秩合并 或者按大小合并均可 bool union_set(int x, int y){ int fx=find_root(x); int fy=find_root(y); if(fx==fy)return false; if(size_set[fx]<size_set[fy]){ swap(fx,fy); } parent[fy]=fx; size_set[fx]+=size_set[fy]; return true; } ``` 上述代码实现了带路径压缩以及按秩/大小合并策略的基础版本,并提供了初始化、查找父节点及合并两部分的功能模块化设计[^2]。 #### 关于C操作的具体细节说明 当执行连接(`C`)命令时,需特别注意顺序问题。如果未预先提取根节点,则应优先调整计数值后再更新父子关系;反之则可能导致逻辑混乱或计算失误。具体表现为以下对比案例分析: - 正确写法: ```cpp if(operate == "C"){ cin >> a >> b; if(find(a) == find(b)) continue; cnt[find(b)] += cnt[find(a)]; p[find(a)] = find(b); } ``` - 错误写法: ```cpp if(operate == "C"){ cin >> a >> b; if(find(a) == find(b)) continue; p[find(a)] = find(b); cnt[find(b)] += cnt[find(a)]; } ``` 前者能够确保即使修改后的指针指向发生变化也不会影响最终统计结果准确性。 ### 结论 综上所述,并查集作为一种高效的数据结构广泛应用于各类实际编程竞赛之中。通过对经典例题如 `[AcWing] 240. 食物链` 的学习研究可以帮助深入理解这一知识点的实际运用价值。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值