点分治/点分树

点分治

额,就是你每次去找一棵树的重心,然后将这棵子树变成以这个重心为根的树,再在这个树上进行某些操作,就可以在 \(O(n\log n)\) 的时间复杂度遍历到任意两个点 \(u,v\)\(P(u,v)\) 上某个点 \(x\) 为根时候的贡献了。那么对于类似于求点对 \((u,v)\) 的某些价值和时,就可以上点分治。差不多吧,没啥用。然后可以去做河童重工。

点分树

点分树相当于动态点分治。对于每次找到的一个重心 \(x\),我们去 \(x\) 为根的子树中又找到了若干个 \(x\) 的儿子为根的子树中的重心 \(v_1\sim v_k\),那么在点分树上就有:\(fa_{v_i(1 \le i \le k)}=x\)。那么现在这棵点分树上任意一个点 \(u\),都有 \(dep_u \le \log n\),那么还有这棵树上所有节点的 \(siz\) 之和是 \(n\log n\) 级别的。性质优美,所以暴力。

那么对于点对 \((u,v)\) 贡献和的问题,我们就可以考虑枚举 \(u\),然后枚举 \(\operatorname{LCA}(u,v)=x\),维护 \(y \in son_x \land \operatorname{LCA}(y,u)\ne y\) 的子树中所有节点的价值,和 \(u\) 一起计算一下贡献。

来点例题/练习题。

P6329

题意是说支持动态修改点权,求 \(\sum \limits_{y=1}^{n} val_y [dis_{x,y}\le k]\)

我们把点分树建出来。先不考虑点权的修改,考虑如何维护 \(x\) 已知时的答案。我们去枚举 \(\operatorname{LCA}(x,y)=u\),记 \(f_{u,k}=\sum\limits_{v=1}^{n}val_v [\operatorname{LCA}(u,v)=u\land dis_{u,v}+1 \le k],g_{u,k}=\sum\limits_{v=1}^{n}val_v [\operatorname{LCA}(u,v)=u\land dis_{u,v}\le k]\)。那么对于当前枚举到的一个 \(u\),假设我们是从 \(v\)\(father\) 上来的,那么有贡献为:\(g_{u,k-len}-f_{v,k-len}\),其中 \(len\)\(x\)\(u\) 的距离。

那么我们只要维护出来 \(f_{u,k}\)\(g_{u,k}\) 就行了。你会发现,\(f_{u,k}=g_{u,k-1}\),然后贡献就行 \(g_{u,k-len}-g_{v,k-len-1}\)。对于一个点 \(u\) 的点权变成 \(val\),它会给到贡献的点 \(v\) 一定是 \(u\) 的祖先。我们去枚举 \(v\),已知了一个路径长度 \(len\),那么 \(k \ge len\) 的都有:\(f_{v,k} \to f_{v,k}+val\)

然后就做完了,可以任意维护 \(f_{i,j}\)。比如对每个 \(i\) 开一棵动态开点线段树。由于每次修改只会更新 \(O(\log n)\) 个节点,每个节点是一个区间加,那么单次修改会最多新增 \(O(\log^2 n)\) 个点。总的点数是 \(O(n\log^2 n)\) 的,时间复杂度 \(O(n\log^2 n)\)

点分治这里有个很容易犯的错误,就是点分树上 \(u\)\(v\) 的路径长度应该是原树上的长度,且对于 \(P(u,v)\) 中的一个点 \(w\),并不一定满足 \(dis_{u,w}+dis_{w,v}=dis_{u,v}\)。然后这个分析的容斥有点问题,但是问题不大,改改就好。

P10603

就是把 P6329 操作反过来。对于枚举的 \(\operatorname{LCA}(x,y)=u\),每次相当于是 \(O(1)\) 次 dfn序区间上 \(dep_v\) 不超过某个值的修改。注意到我们的询问是单点查询,所以可以考虑直接在点 \(u\) 上面加个 tag,表示 \(dep_v\) 不超过某个值的所有点会增加某个值。然后单点查询的时候暴力枚举 \(\operatorname{LCA}\) 算下贡献和就行了。时间复杂度 \(O(n\log^2 n)\)

P10604

\(dis_{x,y}\) 的第 \(k\) 小值。那么当 \(\sum\limits_{i=1}^{n}[dis_{x,y} \le K] <k\) 时,说明第 \(k\) 小值比 \(K\) 大。然后二分即可,时间复杂度 \(O(n\log^3 n)\)

P3241

\(\sum\limits_{y=1}^{n} dis_{x,y}[L \le val_y \le R]\),强制在线。首先这个东西可以转化成求 \(\sum\limits_{y=1}^{n} dis_{x,y}[val_y \le k]\)。然后就是震波了,只是把 \(val\)\(len\) 互换了一下。多记录一个点的数量就行了。

邪恶,这题空间难受。注意到我们没有修改操作,可以考虑将每个节点上有的值放一个 vector 里面排序。那么询问的时候就是一个二分加上前缀和了。因为每个点有的值的数量和是 \(O(n\log n)\) 级别的,所以时间复杂度 \(O(n\log^2 n)\),空间 \(O(n\log n)\)

P3345

相当于是动态的【】吧。我忘了是哪个题了,反正是个换根 DP 板子。

然后那个题有这么个做法,对于当前最优的点 \(x\),如果一个儿子 \(y\)\(x\) 更优,那么答案一定在 \(y\) 的子树里面。额这个证明比较简单,相当于是一个找重心的过程。大概就是你把 \(siz_x\)\(siz_y\) 求出来之后解个 \(f(x) > f(y)\) 的不等式。

然后这题就可以这么做了,我们把点分树建出来。那么每次看当前这个点在原树上是否有一个比它更优的直接连边的点,如果有就跳过去。那么当我们跳到的点是那个点所在子树的重心时,我们最多就只会跳 \(O(\log n)\) 次,然后一次询问的时间复杂度就是 \(O(d\log n)\) 了。再搞个三度化把 \(d\) 消成常数,就可以做到 \(O(\log n)\) 的查询了。

现在考虑求 \(f(x)\)\(f(x)=\sum\limits_{y=1}^{n} dis_{x,y}\times val_y\),那么修改一个点的时候我们暴力跳点分树上的祖先,然后就可以直接加了。时间复杂度 \(O(nd\log^2 n)\sim O(n\log^2 n)\)

P3676

\(\operatorname{LCA(x,a,b)}\) 表示 \(a,b\)\(x\) 为根时候的 \(\operatorname{LCA}\)。那么题面相当于是求:\(\sum\limits_{i=1}^{n}(\sum\limits_{j=1}^{n} val_j[\operatorname{LCA(x,i,j)}=i])^2\)

然后这题就十分困难了。我们记 \(s_{i,x}=\sum\limits_{j=1}^{n}val_j[\operatorname{LCA(x,i,j)=i}]\)。然后我们算 \(\sum\limits_{i=1}^{n}(\sum\limits_{j=1}^{n} val_j[\operatorname{LCA(x,i,j)}=i])^2\) 的时候考虑用 \(s_{i,x}\) 来算。会发现,我们这个式子相当是 \(s_{i,x} \times (sum-(sum-s_{i,x}))\)。然后 \(sum\times \sum\limits_{i=1}^{n}s_{i,x}\),由于每个点会被算 \(dis_{x,i}+1\) 次,所以等价于 \(sum \times \sum\limits_{i=1}^{n}(dis_{x,i}+1)\times val_i=sum^2 +sum\times\sum\limits_{i=1}^{n}dis_{x,i}\times val_i\)。而 \(s_{i,x} \times (sum-s_{i,x})\) 对于求和来说可以看成一个定值,因为这相当于枚举了一条边,然后两边的权值和乘起来再加起来,记这个定值为 \(W\)。那么原问题就等价于求:\(sum^2+sum\times\sum\limits_{i=1}^{n}dis_{x,i}\times val_i -W\)

对于修改一个点 \(x\) 的权值,\(sum\) 是可以 \(O(1)\) 维护的。\(\sum\limits_{}^{}dis_{x,i}\times val_i\) 就是上面一道题。只需要考虑如何维护出 \(W\)。你会发现 \(W\) 实际上相当于对于一条边的两边的集合,任意两个点的点权相乘再求和。那么 \(val_x\) 的修改在与 \(y\) 处于两个不同集合时产生的贡献就会是 \(val_y \times val_x \times dis_{x,y}\)。因为 \(P(x,y)\) 上面任意一条边都会把 \(x,y\) 分到不同集合,而其它情况下 \(x,y\) 处于同一集合。所以我们还是相当于维护一个 \(\sum\limits_{}^{}dis_{x,i}\times val_i\)。然后就可以在 \(O(n\log n)\) 的时间复杂度内做完了。

P5311

感觉这个题很有启发性。

向这种保留一些点,然后求关于 \(x\) 的某些状态的题不好直接维护,那么我们可以考虑一种暴力做法。因为点分树拥有十分优美的性质,所以一些暴力在点分树上可能就对了。

我们把点分树建出来,记 \(mx_{i,j}\)\(i\)\(j\) 路径上最大编号,\(mi_{i,j}\) 为最小编号。那么我们对于一个 \(x\),可以找到一个点分树上的祖先 \(y\),使得 \(l \le mi_{x,y}\land mx_{x,y}\le r\),且 \(y\) 在点分树上深度最小。那么我们所有最终可能与 \(x\) 联通的点一定会在 \(y\) 为根的点分树子树中。因为如果一个 \(y\) 子树以外的点与 \(x\) 联通,说明我们一定经过了 \(P(x,fa_y)\) 中的点,但是这里面至少存在 \(1\) 个点没被保留。

然后我们把点分树拍到 DFS 序上,现在问题就变成了:保留区间 \([l_y,r_y]\) 中满足 \(l\le mi_{x,i} \land mx_{x,i}\le r\) 的点后,颜色的数量。注意到,我们 \(mi_{x,i}\) 可以换成 \(\min(mi_{x,y},mi_{i,y})\)\(mx_{x,i}\) 同理。然后就变成保留区间 \([l_y,r_y]\) 中满足 \(l\le mi_{y,i} \land mx_{y,i}\le r\) 的点后,颜色的数量。我们按照询问的 \(l\)\(mi_{y,i}\) 从大到小排序,那么我们就只需要维护出每个颜色 \(mx_{y,i}\) 的最小值。则对于一个询问,就变成查询有多少个颜色的最小值不超过 \(r\) 了,使用树状数组即可。注意到我们用的是点分树,所以 \(\sum\limits_{y=1}^{n}(r_y-l_y+1)\)\(O(n\log n)\) 级别的,而一个询问最多挂到一个 \(y\) 上面。那么时间复杂度就是 \(O(n\log^2 n)\) 的,空间复杂度 \(O(n\log n)\)

CF757G

支持修改点权。然后求 \(\sum\limits_{y=l}^{r}dis_{x,y}\)。你会发现这个可以转化为 \(\sum\limits_{y=1}^{r}dis_{x,y}-\sum\limits_{y=1}^{l-1}dis_{x,y}\),然后就是 P3345 了,我们在更新点权的时候给每个点搞个动态开点线段树就可以了。时间复杂度 \(O(n\log^2 n)\),为什么有 *3400,感觉和 CF936E 不是一个级别的啊。

CF936E

咕。

不是,这题被办了啊。CF 上面看不了,CF 的题解也没了。啥意思啊。

posted @ 2025-02-21 17:26  harmis_yz  阅读(44)  评论(0)    收藏  举报