题面
思路
思路跟奇偶游戏一样;
不过这里是一个三角结构,不是对称的,需要注意一下;
扩展域并查集
将每个元素分裂成三个条件;
x + B x+B x+B表示 x x x的食物, x + 2 B x+2B x+2B表示 x x x的天敌
Code
#include <iostream>
using namespace std;
const int N = 5e4+10;
const int M = 3*N,B = 5e4;
int n,m,p[M];
int _find(int x){
if(x == p[x]) return x;
return p[x] = _find(p[x]);
}
int main(){
cin >> n >> m;
for(int i=1;i<M;++i) p[i] = i;
int ans = 0;
int d,x,y;
while(m--){
cin >> d >> x >> y;
if(x>n || y>n){
++ans;
continue;
}
if(d == 1){
if(_find(x+B) == _find(y)||
_find(x+2*B) == _find(y)){
++ans;
}else{
//merge
p[_find(x)] = _find(y);
p[_find(x+B)] = _find(y+B);
p[_find(x+2*B)] = _find(y+2*B);
}
}else{
if(x == y){
++ans;
continue;
}
if(_find(y) == _find(x)||
_find(y) == _find(x+2*B)){
++ans;
}else{
p[_find(x)] = _find(y+2*B);
p[_find(x+B)] = _find(y);
p[_find(x+2*B)] = _find(y+B);
}
}
}
cout << ans << '\n';
return 0;
}
边权并查集
我们规定 d ( i ) d(i) d(i)表示点 i i i与其父亲的关系
分为三类,我们用向量来表示两个点之间的关系;
- 0为同类
- 1为食物
- 2为天敌
当
x
,
y
x,y
x,y在同一个集合中,如下图所示;
当
x
x
x吃
y
y
y时,
x
y
=
2
xy=2
xy=2,表示
y
y
y被
x
x
x吃;
当 x x x与 y y y同级, x y = 0 xy=0 xy=0,表示 y y y和 x x x同级;
当 x , y x,y x,y不再同一集合中,我们需要合并;
那么问题就是求解下图中的
d
(
p
x
)
,
p
x
是
x
的
父
节
点
d(px),px是x的父节点
d(px),px是x的父节点
我们设
x
y
xy
xy之间的关系为param
;
同类, p a r a m = 0 param=0 param=0;
y y y被 x x x吃, p a r a m = 2 param=2 param=2
很容易想到
d
y
−
(
d
x
+
d
p
x
)
=
p
a
r
a
m
d_y-(d_x+d_{px}) = param
dy−(dx+dpx)=param(就是向量的表示而已)
解出
d
p
x
d_{px}
dpx即可完成merge
;
Code
#include <iostream>
using namespace std;
const int N = 5e4+10;
int n,m,p[N],d[N];
int _find(int x){
if(x != p[x]){
int root = _find(p[x]);
d[x] = (d[x] + d[p[x]])%3;
p[x] = root;
}
return p[x];
}
int main(){
cin >> n >> m;
for(int i=1;i<N;++i) p[i] = i,d[i]=0;
int ans = 0;
int op,x,y,param;
while(m--){
param = 0;
cin >> op >> x >> y;
if(x>n || y>n){
++ans;
continue;
}
if(op == 2 && x == y){
++ans;
continue;
}
if(op == 2) param = 2;
int px = _find(x),py = _find(y);
if(px == py){
if(((d[y]-d[x])%3+3)%3 != param) ++ans;
}else{
//merge
d[px] = ((d[y]-d[x]-param)%3+3)%3;
p[px] = py;
}
}
cout << ans << '\n';
return 0;
}