食物链 POJ - 1182 (并查集的两种写法)

这是一个非常经典的带权并查集,有两种写法。

1 边权并查集

规定一下,当x和y这条边的权值为0时,表示x和y是同类,当为1时,表示x吃y,当为2时,表示x被y吃。

一共有三种状态,如图,当A吃B,B吃C时,C必须吃A,路径压缩后,A被C吃。

然后就是带权并查集的模板了。

判断条件,当x和y在同一颗树上是,

如果说,x和y之间的关系是0,那么x和RA与Y和RA之间的关系必须相同才行。x和Y之间的关系是1,当S[y]=2时,S[x]=1,当s[y]=1时,s[x]应等于0,才能满足

所以判断条件为(s[x]-s[y]+3)%3=relation.

code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+7;
int fa[N];
int sum[N];
int find(int x){
    if(fa[x]==x) return x;
    else {
        int c=find(fa[x]);
        sum[x]=(sum[x]+sum[fa[x]]+3)%3;
        return fa[x]=c;
    }
}
bool unite(int x,int y,int z){
    int fx=find(x);
    int fy=find(y);
    if(fx!=fy){
        fa[fx]=fy;
        sum[fx]=(sum[y]-sum[x]+z+3)%3;
        return 0;
    }
    else if((sum[x]-sum[y]+3)%3==z) return 0;
    else return 1;
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<=n;i++){
        fa[i]=i;
        sum[i]=0;
    }
    int ans=0;
    int d,x,y;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&d,&x,&y);
        if(x>n||y>n||(d==2&&x==y)) {
            ans++;
            continue ;
        }
        if(unite(x,y,d-1)) ans++;
    }
    printf("%d\n",ans);
    return 0;
} 

2 种类并查集:

思路:将每一个元素拆成3份,x,x+n,x+2*n。分别表示A,B,C

如果x和y为同类,那么x不能和y+n一组,x不能和y+2*n一组。

如果x吃y的话,那么x不能和y一组,x不能呢y+2*n一组。

code:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+7;
int pre[N+N+N];

int find(int x){
    return pre[x]==x? x:pre[x]=find(pre[x]);
}
void unite(int a,int b){
    int x=find(a),y=find(b);
    pre[x]=y;
}

bool same(int x,int y){
    return find(x)==find(y);
}

int main(){
    int n,m;
    cin>>n>>m;
    for(int i=0;i<=n+n+n;i++) pre[i]=i;
    int ans=0;
    for(int i=1;i<=m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        y--;z--;
        if(y>=n||z>=n||y<0||z<0){
            ans++;continue ;
        }
        if(x==1){
            if(same(y,z+n)||same(y,z+2*n)) ans++;
            else {
                unite(y,z);unite(y+n,z+n);unite(y+2*n,z+2*n);
            }
        }
        else {//如果y吃z的话
            if(same(y,z)||same(y,z+2*n)) ans++;
            else {
                unite(y,z+n);unite(y+n,z+2*n);unite(y+2*n,z);
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

原文地址:https://www.cnblogs.com/Accepting/p/12663140.html

时间: 2024-08-02 21:32:55

食物链 POJ - 1182 (并查集的两种写法)的相关文章

可持久化并查集的两种实现

对于像UVA 11987 Almost Union-Find这样的题目,要求把一个元素从一个集合中剥离的情况,我们只需要新建一个节点然后---. 还是看代码吧: inline void move(int x,int y) // 把x从y集合中剥离 { int fx = find(id[x]),fy = find(id[y]); if(fx == fy) return ; cnt[fx] --,sum[fx] -= x; id[x] = ++ tot; // 可持久化 f[id[x]] = fy;

POJ 1182[并查集]

动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种. 有人用两种说法对这N个动物所构成的食物链关系进行描述: 第一种说法是"1 X Y",表示X和Y是同类. 第二种说法是"2 X Y",表示X吃Y. 此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的.当一句话满足下列三条之一时,这句话就是假话,否

源哥每日一题第十八弹 poj 1182 并查集

题目链接:http://poj.org/problem?id=1182 题意:看不懂?退群吧 比平常的并查集加了一个判断集合间关系的操作: 开一个数组记录当前点所在集合的次序(第几个集合)用012表示 比较简单的思路,不过体现了并查集的精妙 #include <iostream> #include <cstring> #include <cstdio> using namespace std; int dad[500005]; int st[50005]; int fi

poj 1182 并查集高级应用

C - 是谁站在食物链的顶端 Crawling in process... Crawling failed Time Limit:1000MS     Memory Limit:10000KB     64bit IO Format:%lld & %llu Submit Status Description Input Output Sample Input Sample Output Hint Description 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B,

并查集的两种实现(按秩合并+路径压缩)

并查集:就是有求并集,查找元素属于哪个集合的功能. 1.路径压缩:使X到根上的每一个节点的父节点都变为根节点. 查询: void Find(int x) { if(a[x]==0) return x; else return a[x]=Find(a[x]); } 合并: void Merge(int x,int y) { int t1=Find(x),t2=Find(y); if(t1!=t2) a[t1]=t2; } 2.按秩合并:使较浅的树成为较深的树的子树. 查询: void Find(i

POJ 2524 并查集

Ubiquitous Religions Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 23580 Accepted: 11609 Description There are so many different religions in the world today that it is difficult to keep track of them all. You are interested in finding o

for循环的两种写法

教程 (https://tour.golang.org/methods/21) 里的 for 是这样写的: 其中 for 语句可以改写如下: for n, err := r.Read(b); err != io.EOF; n, err = r.Read(b) { fmt.Printf("n = %v err = %v b = %v\n", n, err, b) fmt.Printf("b[:n] = %q\n", b[:n]) } (当然,golang 里的 for

sql sever 的两种写法

事务(Transaction)是并发控制的单位,是用户定义的一个操作序列.这些操作要么都做,要么都不做,是一个不可分割的工作单位. 通过事务,SQL Server能将逻辑相关的一组操作绑定在一起,以便服务器保持数据的完整性. 在sql server+ .net 开发环境下,有两种方法能够完成事务的操作,保持数据库的数据完整性: 一个就是用sql存储过程,另一个就是在ADO.NET中一种简单的事务处理: 现在通过一个典型的银行转账的例子来说明一下这两个例子的用法 我们先来看看sql存储过程是如何来

angularJs中$q的两种写法

带缓存处理的两种写法 过程:点击button触发load()方法,请求数据成后显示到页面中.如果已经请求过则从缓存中读取. 在线浏览 写法1: function demo(){ if (demo.cache == undefined) { return $http.get('https://api.github.com/users/github') .success(function(data, status, headers){ demo.cache = data; return $q(fun