牛客-食物链 (扩展域并查集 / 带权并查集)

题目: 食物链
题解:
1.扩展域并查集

由题可知,动物无非A、B、C三类,A吃B,B吃C,C吃A
我们可以开一个3*5e4的数组 1 ~ 5 ∗ 1 0 4 区 间 代 表 本 身 一 类 x , 5 ∗ 1 0 4 ~ 10 ∗ 1 0 4 区 间 代 表 被 x 吃 掉 的 一 类 , 10 ∗ 1 0 4 ~ 15 ∗ 1 0 4 代 表 能 吃 x 的 一 类 {1~5*10^4区间代表本身一类x,5*10^4 ~ 10*10^4区间代表被x吃掉的一类,10*10^4~15*10^4代表能吃x的一类} 15104x510410104x1010415104x
如果x和y从属于一个区间,那么fa[x]=y代表x和y从属与一类。
如果x和y不属于一个区间,那么fa[x]=y代表y吃x。

如果x吃y,那么x与被x吃掉、吃x与x以及被x吃与吃x,这三部分可以分别看为x和y的关系,然后fa[find(x)]=find(y+n),fa[find(x+n)]=find(y+2n),fa[find(x+2n)]=find(y)。

如果x和y是同类,那么fa[find(x)]=find(y)、fa[find(x+n)]=find(y+n)、fa[find(x+2n)]=find(y+2n),代表它们本身、被它们吃的一类以及吃掉它们的一类各属于一类。

判断一个语句是否为真,则只要判断它的反例是否成立。
题目中有两种情况:

  1. 第一种:如果X和Y是同类,那么可判断X吃Y或者Y吃X,就可以知道此话是否为真。
  2. 第二种:如果X吃Y,那么“X和Y是同类”或“Y吃X”都是谎言。

2.带权并查集

带权并查集目前大部分维护x到f[x]的距离,此题也不例外。

d[x]:维护点x到f[x]的距离模3。
很明显题目中x和f[x]有三种关系,在此可设
1:可以吃根结点
2:被根结点吃
0:和根结点同一类
三者的关系:1类点吃0,0吃2,2吃1。

不难发现,我们可以d[x]模3,就可以将所有点分为这三类。
那么如何合并更新d[x]值呢。

  1. 如果操作一,x和y是同一类

    当 fx==fy:则x和y属于同一个集合,d[x]=d[y]mod3
    否则,将两个集合合并(取将fx合并到fy上,反之亦然),此时需要更新d[fx]
    如图所示d[fx]=(d[y]-d[x]+3)%3
    在这里插入图片描述

  2. 如果操作二,x吃y

    当 fx==fy:则若x是0,y是2;若x是1,y是0等等,易得满足关系d[x]=(d[y]+1)mod3
    否则,将两个集合合并(取将fx合并到fy上,反之亦然),此时需要更新d[fx]
    如图所示d[fx]=(d[y]+1-d[x]+3)%3
    在这里插入图片描述

代码

扩展域并查集

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<cassert>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<deque>
#include<iomanip>
#include<list>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
using namespace std;
//extern "C"{void *__dso_handle=0;}
typedef long long ll;
typedef long double ld;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define pii pair<int,int>

const double PI=acos(-1.0);
const double eps=1e-6;
const ll mod=1e9+7;
const int inf=0x3f3f3f3f;
const int maxn=15e4+10;
const int maxm=100+10;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

int fa[maxn];
int n,k;
int find(int x)
{
	return fa[x]==x ? x :fa[x]=find(fa[x]);
}
void mergc(int x,int y)
{
	fa[find(x)]=find(y);
}

int main()
{
	
	cin >> n >> k;
	int cnt=0;
	for(int i=0;i<3*n;i++) fa[i]=i;
	for(int i=0;i<k;i++)	
	{
		int d,x,y;
		cin >> d >> x >> y;
		if(x>n || y>n) { cnt++;continue; }
		if(d==1)
		{
			if(find(x) == find(y+n) || find(x) == find(y+2*n)){
				cnt++; continue;
			}
			mergc(find(x),find(y));
			mergc(find(x+n),find(y+n));
			mergc(find(x+2*n),find(y+2*n));
		}
		else
		{
			if(find(x) == find(y) || find(x)==find(y+2*n)){
				cnt++; continue;
			}
			mergc(find(x),find(y+n));
			mergc(find(x+n),find(y+2*n));
			mergc(find(x+2*n),find(y));	
		}
	}
	cout << cnt << endl;
}

带权并查集

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<cassert>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<deque>
#include<iomanip>
#include<list>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#include<unordered_set>
#include<unordered_map>
using namespace std;
//extern "C"{void *__dso_handle=0;}
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define pii pair<int,int>
#define lowbit(x) x&-x

const double PI=acos(-1.0);
const double eps=1e-6;
const ll mod=1e9+7;
const int inf=0x3f3f3f3f;
const int maxn=5e4+10;
const int maxm=100+10;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int f[maxn],d[maxn];
int find(int x)
{
	if(x!=f[x])
	{
		int rt=find(f[x]);
		d[x]=(d[x]+d[f[x]]+3)%3;
		f[x]=rt;
	}
	return f[x];
}
int main()
{
	int n,k; scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) f[i]=i;
	int ans=0;
	while (k--) {
		int op,x,y;
		scanf("%d%d%d",&op,&x,&y);
		if(x>n || y>n) { ans++; continue;}
		int fx=find(x),fy=find(y);
		if(op==1)
		{
			if(fx==fy && (d[x]-d[y]+3)%3) ans++;
			else if(fx!=fy)
			{
				d[fx]=(d[y]-d[x]+3)%3;
				f[fx]=fy;
			}
		}
		else {
			if(fx==fy && (d[x]-d[y]-1)%3) ans++;
			else if(fx!=fy)
			{
				d[fx]=(d[y]+1-d[x]+3)%3;
				f[fx]=fy;
			}
		}
	}
	printf("%d\n",ans);
}

/*
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
*/
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值