题目
分析
给每个动物赋予三个节点(n,2n,3n),那就将一个动物扩展到了3个(x——>x_A 、x_B
x_C),这样就将并查集的节点数量扩展到3n,用并查集维护这 3n 个节点的信息。
(如果不太了解并查集的代码,可以参考下我的另外一篇博文:并查集简单介绍)
那么把题目中的给的 K 条信息转换一下(且用并查集维护这个关系):
- 如果 x 和 y 两个动物是同类:
(1)x_a 和 y_a 是同类;合并 x_a 和 y_a 。
(2)x_b 和 y_b 是同类;合并 x_b 和 y_b 。
(3)x_c 和 y_c 是同类;合并 x_c 和 y_c 。 - 如果 x 吃 y 说明:
(1)x_a 吃 y_b;合并 x_a 和 y_b。
(2)x_b 吃 y_c; 合并 x_a 和 y_b。
(3)x_c 吃 y_a;合并 x_a 和 y_b。 - 如果 x 和 y 一定不是同类,说明存在以下情况:
(1)要么 x 吃 y
(eg:x_a 吃 y_b , 则x_a 和 y_b 在一个集合里…)
(2)要么 y 吃 x
(eg:y_a 吃 x_b ,则y_a 和 x_b 在一个集合里…) - 如果 x 一定不吃 y ,说明存在以下情况:
(1)要么 x 和 y 同类
(eg:x_a 和 y_a 是同类,则x_a 和 y_a 在一个集合里…)
(2)要么 y 吃 x
(eg:y_a 吃 x_b ,则y_a 和 x_b 在一个集合里…)
坑:注意这里的输入用scanf
或者 用cin
加上std::ios::sync_with_stdio(false);
不然会超时,通不过。
AC代码
#include<iostream>
#define Max_K 100000+5
#define Max_N 150000+5
using namespace std;
int T[Max_K],X[Max_K],Y[Max_K];//记录输入的K条信息
int N,K;//N个动物 K条信息
int par[Max_N];//记录每个结点i的父亲 ;每个动物有3个结点,表示ABC三类,x,x+N,x+2*N分别代表x-A x-B x-C
int rank[Max_N];//树的高度
void init(int n) { //初始化
for(int i=0; i<n; i++) {
par[i]=i;
rank[i]=0;
}
}
int find(int x) {//查找根节点
if(par[x]==x)
return x;
return par[x]=find(par[x]);
}
void unite(int x,int y) {//合并
x=find(x);
y=find(y);
if(x==y)
return ;
if(rank[x]<rank[y])
par[x]=y;
else {
par[y]=x;
if(rank[x]==rank[y])
rank[x]++;
}
}
bool same(int x,int y) {//判断是否同一类
return find(x)==find(y);
}
void solve() {
init(N*3);//初始化合并集,每个动物有3个结点,每个动物都有属于ABC三类,x,x+N,x+2*N分别代表x-A x-B x-C
int ans=0;
for(int i=0; i<K; i++) { //循环开始判断K条信息的正确与否
int t=T[i];
int x=X[i]-1,y=Y[i]-1;//把输入的动物编号改为0~N-1
if(x<0 ||N<=x||y<0||N<=y) { //该动物编号 不合法
ans++;
continue;
}
if(t==1) { //x y是同类?
if(same(x,y+N)||same(x,y+2*N)) { //如果x吃y 或者y吃x 说明是假话
ans++;
} else { //同类合并:每个x_ABC 和y_ABC 都合并为一类
unite(x,y);
unite(x+N,y+N);
unite(x+N*2,y+N*2);
}
} else { //是x吃y的关系?
if(same(x,y)||same(x,y+2*N)) { //如果x,y是同类 或者是y吃x的关系 说明是假话
ans++;
} else {//是吃的关系,将x与y的有可能的吃的关系都合并
unite(x,y+N);//x_A吃y_B
unite(x+N,y+2*N);//x_B吃y_C
unite(x+N*2,y);//x_C吃y_A
}
}
}
printf("%d\n",ans);
}
int main() {
scanf("%d%d",&N,&K);// //N个动物 K条信息
for(int i=0; i<K; i++) {
scanf("%d%d%d",&T[i],&X[i],&Y[i]);
}
solve();
}
收获
- 并查集的运用。
- 通过扩充动物来实现更简单的维护关系。