题目传送门
带权并查集
关系判断数组:
0 : 本节点与父节点是同类
1 : 本节点吃父节点
2 : 本节点被父节点吃
注:题目所给的两种关系序号需预处理减掉1。
即,x与y同类:1-1=0。x吃y:2-1=1
状态压缩时的关系转移方程:
int tmp = f[x]; // important
f[x] = find (f[x]);
rlt[x] = (rlt[x] + rlt[tmp]) % 3;
注:要在更新父节点后才能更新关系
两棵树合并:
void Union (int c,int a,int b)
{
int A = find (a);
int B = find (b);
if (A != B)
{
f[A] = B;
rlt[A] = (rlt[b] - rlt[a] + c + 6) % 3;
}
}
注:合并a,b两不在同一棵树上的节点时,
是将a的祖先连到b节点上。
代码:
#include
#include
#include
#include
#include
#define N 55555
using namespace std;
int n,f[N];
int rlt[N];
int find (int x)
{
if (f[x] == x)
return x;
int tmp = f[x]; // important !
f[x] = find (f[x]);
rlt[x] = (rlt[x] + rlt[tmp]) % 3;
return f[x];
}
void Union (int c,int a,int b)
{
int A = find (a);
int B = find (b);
if (A != B)
{
f[A] = B;
rlt[A] = (rlt[b] - rlt[a] + c + 6) % 3;
}
}
bool check (int c,int a,int b)
{
if ((a == b && c == 1) || a > n || b > n)
return false;
int A = find(a);
int B = find(b);
if (A != B)
return true;
return (rlt[a] - rlt[b] + 6)% 3 == c;
}
int main ()
{
int m,a,b,c;
scanf ("%d%d",&n,&m);
for (int i=0;i<=n;i++)
{
f[i] = i;
rlt[i] = 0;
}
int cnt = 0;
for (int i=1;i<=m;i++)
{
scanf ("%d%d%d",&c,&a,&b);
c--;
if (check(c,a,b))
Union (c,a,b);
else
cnt++;
}
printf ("%d\n",cnt);
return 0;
}
带权并查集还很多没理解的地方,
先挖个坑,
记得起来就来填。