HDU 3038 带权并查集裸题

来源:互联网 发布:seo app 编辑:程序博客网 时间:2024/05/19 04:29

花了好长时间才明白这个到底是怎么回事。委屈

权值其实就是各个子节点与父节点的相对距离。

带权就是表示连接到父亲节点的子节点的那条边是有权值的,我们用另外一个数组来记录这个权值(或者你用结构体也行)。

个人习惯用w数组记录相对距离值,比如,w[2]=3表示序号为2的节点到其父亲的相对距离是3.

并查集这个pre数组中有很多颗树,每棵树都有自己的根节点,这一点和原先的不带权值的并查集没有区别。

有区别的是我们要记录子节点到父亲节点的相对距离。

我们所要维护的不止是树这个结构还要维护到根的距离这个东西。


带权并查集和不带权并查集的小比较

举个例子:

一、a、b、c在同一棵树上,a是根,b、c都是叶子;d、e、f在同一树上,d是根,e、f是叶子,没有附加的东西了,这个叫没权值的并查集,假设我要把c和e关联在一起,我只需要把他们弄到同一棵树上就可以了,不需要考虑他们到父节点点的相对距离。

二、a、b、c在同一棵树上,a是根,b、c都是叶子,b到a的相对距离是11,c到a的相对距离是12;d、e、f在同一树上,d是根,e、f是叶子,e到d的相对距离是13,f到d的相对距离是14。现在关联c和e:e比c远A。这样两棵树就因为这个关系要合并成一棵树了,用朴素的合并方法,只要把a节点(c的根节点)和d节点(e的根节点)连接起来,以d作为整棵树的根节点,然后通过换算(到底怎么算?下文有。吐舌头)更新a到d的相对距离,这样就可以完成合并了。


正文:

一、各种操作:

1.找根节点,顺带路径压缩,体现在Find函数中。

2.合并两棵树,体现在unite函数中。

3.初始化。

这三个操作都是必要的。


二、Find函数:

路径压缩的引导就不必要了,直接说方法就可以。

因为要维护一个相对距离,我们让各个子节点认祖宗为父亲节点时,要把沿途子节点的所有父节点的权值都加上。

代码实现起来还是用了递归:

int Find(int x){    if(x==pre[x]) return pre[x];    int t=Find(pre[x]);    w[x]+=w[pre[x]];    return pre[x]=t;}

除去第三行,你会发现和原先不带权的并查集的Find函数一样微笑,这里只不过是把沿途的父节点的权值都加上了而已。


三、unite函数:

还是传统的联结两棵树的根节点

void unite(int a,int b,int weight){    int fa=Find(a);    int fb=Find(b);    if(fa!=fb)    {        pre[fb]=fa;        w[fb]=w[a]-w[b]+weight;    }}

这里要提醒一点,这个保证给出的联结条件是正确的,有些题目是需要判断这些联结条件是否正确。


那么上文提到了“通过某种换算”可以算出两个根节点的相对距离,到底怎么算?

举个例子:

还是上文提到的,最好自己手动画两棵树哦~微笑

a、b、c在同一棵树上,a是根,b、c都是叶子,b到a的相对距离是11,c到a的相对距离是12;d、e、f在同一树上,d是根,e、f是叶子,e到d的相对距离是13,f到d的相对距离是14。现在关联c和e:e比c的远10(e-c=10)(注意这里不能简单说e与c的距离是10,这样给的信息只有相对距离而没有说谁离根更远),这对应于代码中就是参数为(c,e,10)参数用数字表示了节点序号,我用了字母表示了节点序号。


假设参数b离根节点更远,我们采用的是左归并,即右面的树归并到左边的树上。

e-c=10,对应于参数来说就是(c,e,10)注意参数是要有严格的远近顺序的。


还是经典的合并,我想要a对d的相对距离,也就是a比d远多少。

我们已经知道c比a远12(w[c])了,e比c远10(weight),那么e比a远10+12(weight+w[c]),我们本身知道了e比d远13(w[e])

那么我们想求的a比d远的值就是e比a远的减去e比d远的(weight+w[c]-w[e])。


或者你也可以解方程组,也挺简单的。


对应到代码上,就是

也就是weight+w[a]-w[b];


以后更新,还有些问题……

原创粉丝点击