- 把每个动物x拆分成3个域:x_self,x_eat,x_enemy
- 若x,y是同类,合并x_self与y_self,x_eat与y_eat,x_enemy与y_enemy
- 若x吃y,合并x_self与y_enemy,x_eat与y_self,x_enemy与y_eat
- 给出信息x,y同类时,先判断是否有x_self与y_eat同一集合,x_eat与y同一集合;给出信息x吃y时,判断是否有x_self与y_self同一集合,x_self与y_eat同一集合。
#include<bits/stdc++.h>
#define ll long long
#define INIT(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=2e5+7;
const int M=5e4;
int f[N],n;
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
bool merge(int x,int y,int d){
int x_eat=x+n,x_ene=x+2*n;
int y_eat=y+n,y_ene=y+2*n;
if(d==1){
if(find(x)==find(y_eat)||find(x_eat)==find(y)) return false;//x吃y或y吃x
if(find(x)==find(y))return true;
f[find(x)]=find(y);
f[find(x_ene)]=find(y_ene);
f[find(x_eat)]=find(y_eat);
}
else {
if(find(x)==find(y)||find(x)==find(y_eat)) return false;//x,y同类或y吃x
if(find(x)==find(y_ene))return true;
f[find(x)]=find(y_ene); //x与y的天敌同类
f[find(x_eat)]=find(y); //y与x的食物一类
f[find(x_ene)]=find(y_eat); //x的天敌与y的食物一类,x->y->z->x
}
return true;
}
int main(){
int k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n*3;i++) f[i]=i;
int x,y,d,ans=0;
while(k--){
scanf("%d%d%d",&d,&x,&y);
if(x>n||y>n||(d==2 && x==y)) {
ans++;
continue;
}
if(!merge(x,y,d)) ans++;
}
printf("%d\n",ans);
return 0;
}
- 种类并查集,d[x]=0表示x与f[x]同类,d[x]=1表示x吃f[x],d[x]=2表示x被f[x]吃
- 合并和判断的时候画一下矢量图就ok了
#include<bits/stdc++.h>
#define ll long long
#define INIT(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=5e4+7;
int f[N],n,d[N];
int find(int x){
if(x==f[x])return x;
int root=find(f[x]);
d[x]=(d[x]+d[f[x]])%3;
return f[x]=root;
}
bool merge(int a,int b,int c){
int fa=find(a),fb=find(b);
if(fa==fb){
if(d[a]!=(d[b]+c)%3)return false;
return true;
}
d[fa]=(c+d[b]-d[a]+3)%3;
f[fa]=fb;
return true;
}
int main(){
int k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) f[i]=i;
int x,y,d,ans=0;
while(k--){
scanf("%d%d%d",&d,&x,&y);
if(x>n||y>n||(d==2 && x==y)) {
ans++;
continue;
}
if(!merge(x,y,d-1)) ans++;
}
printf("%d\n",ans);
return 0;
}
tql
这就是传说中的拓展域并茶几写法吗