题目: 食物链
题解:
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的一类}
1~5∗104区间代表本身一类x,5∗104~10∗104区间代表被x吃掉的一类,10∗104~15∗104代表能吃x的一类。
如果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),代表它们本身、被它们吃的一类以及吃掉它们的一类各属于一类。
判断一个语句是否为真,则只要判断它的反例是否成立。
题目中有两种情况:
- 第一种:如果X和Y是同类,那么可判断X吃Y或者Y吃X,就可以知道此话是否为真。
- 第二种:如果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]值呢。
-
如果操作一,x和y是同一类
当 fx==fy:则x和y属于同一个集合,d[x]=d[y]mod3
否则,将两个集合合并(取将fx合并到fy上,反之亦然),此时需要更新d[fx]
如图所示d[fx]=(d[y]-d[x]+3)%3
-
如果操作二,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
*/