1.father[x]表示x的根节点,rank[x]表示x与father[x]之间的关系,x-->0-->father[x] 表示x与father[x]同类,x-->1-->father[x]表示x吃father[x],x-->2-->father[x]表示x被father[x]吃
2.若已知x-->r1-->y, y-->r2-->z,那么如何求得x-->?-->z呢? 可以求得x-->(r1+r2)%3-->z。
根据A吃B,B吃C,则C吃A可以获得。
3.对于find_set函数,不仅要更新x的father[x],还要更新rank[x]。
由x-->rank[x]-->father[x]和father[x]-->rank[father[x]]-->father[father[x]]可以得到x-->(rank[x]+rank[father[x]])%3-->father[father[x]].
4.对于不在一个集合的x、y,则应合并X的根节点和Y的根节点,同时修改各自的rank。那么问题来了,合并了之后,被合并的根节点的kind值如何变化呢?
现有x和y,d为x和y的关系,xf和yf分别是x和y的根节点,于是我们有x--rank[x]-->xf,y--rank[y]-->yf,显然我们可以得到xf--(3-rank[x])-->x,yf--(3-rank[y])-->y。假如合并后x为新的树的根节点,那么原先fx树上的节点不需变化,yf树则需改变了,因为rank值为该节点和树根的关系。这里只改变rank(yf)即可,因为在进行find_set操作时可相应改变yf树的所有节点的kind值。于是问题变成了yf--?-->xf。我们不难发现yf--(3-rank[y])-->y--(3-d)-->x--rank[x]-->xf,根据前面的结论,我们有yf--(3-rank[y])-->y--(3-d)-->x--rank[x]-->xf。我们求解了xf和yf的关系了。
#include <stdio.h>
#define N 50010
int father[N+10], rank[N+10];
void init(int n)
{
for (int i = 0; i <= n; i++)
{
father[i] = i;
rank[i] = 0;
}
}
int find(int x)
{
if (x != father[x])
{
int t = father[x];
father[x] = find(father[x]);
rank[x] = (rank[x] + rank[t]) % 3;
}
return father[x];
}
void unionSet(int x, int y, int d)
{
int xf = find(x);
int yf = find(y);
if (xf != yf)
{
father[xf] = yf;
rank[xf] = (3-rank[x]+d-1+rank[y]) % 3;
}
}
int main()
{
int n, k;
scanf("%d%d", &n, &k);
int d, x, y;
int count = 0;
init(n);
for (int i = 0; i < k; i++)
{
scanf("%d%d%d", &d, &x, &y);
if (x > n || y > n || (d == 2 && x == y))
{
count++;
//printf("error\n");
}
else
{
int xf = find(x);
int yf = find(y);
if (xf == yf)
{
if ((3-rank[x]+d-1+rank[y]) % 3 != 0)
{
count++;
//printf("error\n");
}
}
else
{
unionSet(x, y, d);
}
}
}
printf("%d\n", count);
return 0;
}