题目
思路一
如何将不同并查集合并,对于同类(1)来说:
对于不同类(2)来说:
代码1
#include<iostream>
using namespace std;
const int N=50005;
int p[N],d[N];
int n,m;
int find(int x){//记录到根节点的距离
if(p[x]!=x){
int t=find(p[x]);
d[x]+=d[p[x]];
p[x]=t;
}
return p[x];
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
p[i]=i; //所有的父节点都是自己
}
int res=0;
while(m--){
int t,x,y;
cin>>t>>x>>y;
if(x>n || y>n){ //x,y大于n的范围,这句话是假话
res++;
}else{
int px=find(x);//x的根节点
int py=find(y);//y的根节点
if(t==1){//x和y是同类
if(px==py && (d[x]-d[y])%3!=0){ //在同一棵树上,两个点的距离差%3不为0,说明不是同类
res++;
}else if(px != py){ //x,y不在同一颗树上
p[px]=py; //x的根节点连在y的根节点上
d[px]=d[y]-d[x];
}
}else{//x吃y
if(px==py && (d[x]-d[y]-1)%3!=0){ //x,y在同一颗树上,x的距离比y大一,即(dx-dy-1)%3==0
res++;
}else if(px != py){//x,y不在同一颗树上,
p[px]=py;//x的根节点连在y的根节点上
d[px]=d[y]+1-d[x];
}
}
}
}
cout<<res<<endl;
return 0;
}
思路二
并查集(拓展域)
这道题目我们主要是要开三个拓展的域,也就是天敌域,同类域,以及捕食域.
如果x,y是同类,但是x的捕食域
有y,那么❎
如果x,y是同类,但是x的天敌域
有y,那么❎
如果x,y是同类,但是x的同类域
有y,那么✅
如果x是y的天敌,但是x的同类域
中有y,那么❎
如果x是y的天敌,但是x的天敌域
中有y,那么❎
如果x是y的天地,但是x的捕食域
中有y,那么✅
我们让当前X的捕食域为X+N,当前X的天敌域为X+N+N
代码2
//我们让当前X的捕食域为X+N,当前X的天敌域为X+N+N
#include<iostream>
using namespace std;
const int N=50000*3+5;
int p[N];
int n,m;
int find(int x){
if(x!=p[x]){
p[x]=find(p[x]);
}
return p[x];
}
int main(){
cin>>n>>m;
for(int i=1;i<=3*n;i++){
p[i]=i;
}
int res=0;
while (m -- ){
int k,x,y;
cin>>k>>x>>y;
if(x>n || y>n){
res++;
}else if(k==1){//x和y同类
if(find(x)==find(y+n) || find(x)==find(y+n+n)){//x在y的捕食域中,x在y的天敌域中
res++;
}else{
p[find(x)]=p[find(y)]; //x和y同类
p[find(x+n)]=p[find(y+n)]; //x的捕食域和y的捕食域同类
p[find(x+n+n)]=p[find(y+n+n)]; //x的天敌域和y的天敌域同类
}
}else{ //x是y的天敌
if(x==y || find(x)==find(y) || find(x)==find(y+n)){ //x和y是同一个,x和y是同类,x是y的捕食域
res++;
}else{
p[find(x)]=p[find(y+n+n)]; //y的天敌域中加入x
p[find(x+n)]=p[find(y)]; //x的捕食域中加入y
p[find(x+n+n)]=p[find(y+n)]; //x的天敌域和y的捕食域是同类
}
}
}
cout<<res<<endl;
return 0;
}