p:储存下标的根节点是什么, 当输入一个x的时候, px = find(x),先找到x的根节点,在find函数里已经将x归到了它的根树上了,(往上递归直至一个根返回本身)
d[x]:表示x到其父节点的距离,不是根节点!!!
(根节点指祖宗,最上方的, 一个集合里面只有一个;
父节点指调用p的表示,仅为上一层的)
怎么理解find函数???
find函数有两个作用:①路径压缩(找x的根节点) ②更新权值d[x]
·为什么要先 t = find(p[x])?
关键就是既要先执行find(p[x]),又要让d[x] += d[p[x]]中p[x]的值保持不变
递归 find(f[x])的过程就是路径压缩
我们来看一下如果不这样做,
int find(int x)
{
if (p[x] != x)
{
d[x] += d[p[x]];//这里的d[p[x]]只是x到父节点的距离,不是到根节点的距离
p[x] = find(p[x]);
}
return p[x];
}
我们发现既要让d[p[x]]正确找到他到根节点的距离, 又要让p[x]暂时不变,否则d[x]加的就不是父节点到根节点的距离了,而是根节点到根节点的距离。
既要?又要? 令人头痛。。。
因此,我们优雅的 暂存 t = find(p[x]), d更新了,p也没变
举个栗子::
假设有一棵树 a -> b -> c -> d, 根节点为 d。d[b]一开始等于 b、c 之间的距离,再执行完路径压缩命令之后,d[b] 等于b、d之间的距离。 d[a] += d[b]: 为了确保d[a]等于 节点a、d的距离,d[b]必须等于b 、d的距离,所以要先调用find(b)更新d[b], 同时p[x] = find(b)会改变p[x]的值,结果就会变成d[a] += d[d],所以先用一个变量把p[a]的值存起来。 关键就是既要先执行find(p[x]), 又要让d[x] += d[p[x]]中p[x]的值保持不变
这样我们就能全面的理解这段代码了。
先调用再递归
int find(int x)
{
if (p[x] != x)
{
int t = find(p[x]);
d[x] += d[p[x]];
p[x] = t;
}
return p[x];
}
手动模拟find
路径压缩后
根据权值取模3 就很好判断关系了
https://www.acwing.com/activity/content/code/content/9126551/