思路:
(1)细数本题操作:
- 对于输入x,y :若未建立联系,则建立关系网;若已经建立联系,则判断联系是否一致。
- 则分为:①判定是否建立联系;②建立联系;③查询联系;三种操作。
(2)注意到输入节点联系可画成树的形式,且相互联系可以转化为与根节点之间的距离进行描述。
(3)于是考虑并查集,通过是否在同一集合内判定是否建立了联系;通过集合合并及距离转换建立联系;通过查询距离查询联系。
(4)距离与联系的关系:用d[x]描述x与其父亲节点之间的距离,当调用find(x)函数进行路径压缩时,x认祖宗为父亲,则d[x]加上原父亲到祖宗之间的距离;由于每次判定都要先找x,y的祖宗,所以一定已经进行了路径压缩,则此时拿到的d[x],d[y]已经是x,y与祖宗之间的距离了;而:
- (d[x] - d[y]) %3 == 0 则x,y同类;
- (d[x] - d[y]) %3 == 1则x吃y;
- (d[x] - d[y]) %3 == 2则y吃x;
通过以上关系:
①判定是否建立联系: px == py;
②建立联系:
在p[px] = py合并后,
- 同类:需满足(d[x] + d[px] - d[y])%3 == 0; d[x]还未路径压缩,父亲未变,不需要处理,但px父亲已变,所以令d[px] = d[y] - d[x];
- x吃y:需满足(d[x] + d[px] - d[y])%3 == 1; d[x]还未路径压缩,父亲未变,不需要处理,但px父亲已变,所以令d[px] = d[y] + 1 - d[x];
③查询联系:
- 若t == 1表示判定为同类,(d[x] + d[px] - d[y])%3 == 0表示实际为同类若不一致则为假;
- 若t== 2表示判定x吃y,(d[x] + d[px] - d[y])%3 == 1表示实际x吃y,若不一致则为假;
代码:
#include<iostream>
using namespace std;
const int N = 5e4 + 10;
int p[N],d[N];
int find(int x)
{
if(x != p[x])
{
int t = p[x];
p[x] = find(p[x]);
d[x] += d[t];
}
return p[x];
}
int main()
{
int n,k;
cin >> n >> k;
for(int i = 1;i <= n;i ++)
{
p[i] = i;
d[i] = 0;
}
int res = 0;
while(k --)
{
int t,x,y;
cin >> t >> x >> y;
int px = find(x),py = find(y);
if(x > n || y > n)
res ++;
else
{
if(px == py)//查询关系
{
if(t == 1)//判断同类
{
if((d[x] - d[y]) %3 != 0)res ++;
}
else
{
if((d[x] - d[y] - 1) %3 != 0) res ++;
}
}
else//添加关系
{
if(t == 1)
{
p[px] = py;
d[px] = d[y] - d[x];
}
else
{
p[px] = py;
d[px] = d[y] - d[x] + 1;
}
}
}
}
cout << res << endl;
return 0;
}