题目
点这里看题目。
BZOJ 目测…是炸了。
分析
动态点分治入门题。
首先理解什么叫 " 动态点分治 "。
一般点分治需要离线解决,不带修改。动态点分治可以用点分治的方法在线解决问题,支持修改。
在点分治的过程中,每个点都会成为一次分治中心进行计算。如果我们将点按照计算顺序连成一棵树的话,我们就会得到原树的一颗 " 虚树 " , 我们称之为点分树。煮个栗子:
可以发现,由于重心的先天性质,点分树的树高为 O ( log 2 n ) O(\log_2n) O(log2n)。
对于本题,由于只会有单点修改(例如修改 u u u),所以我们可以在点分树上从 u u u开始,对于 u u u到点分树的根的路径进行暴力修改,这样只会有 O ( log 2 n ) O(\log_2n) O(log2n)个点。再深的点不会受到影响(因为对于更深的点,它们进行点分治的时候, u u u已经没有了)。
如何维护信息?我们对于点分树上每一个点 u u u维护两个树状数组,第一个树状数组维护下标为到 u u u距离的权值和,用于直接计算贡献;第二个树状数组维护下标为到 u u u点分树上父亲距离的权值和,用于容斥扣除重复贡献。统计的方式类似于修改,我们在点分树上从 u u u开始上跳,用树状数组计算贡献。再煮个栗子:
现在想必各位都懂了。
需要注意的几点:
1. 注意点分树上,祖先到自己的距离并不是单调递增的。因此不能中途 break。
2. 由于我们只需要知道点分树上祖先到自己的距离和祖先的标号,且点分树深度有限,因此我们把这两个值用数组存下来即可。
3. 树状数组需要用 vector 存。仅存下需要的空间会优化到 O ( n log 2 n ) O(n\log_2n) O(nlog2n)(跟点分治基础时间复杂度一样),而不优化会变成 O ( n 2 ) O(n^2) O(n2), MLE。
4. 树状数组注意下标不要变成非正数。
5. 函数千万千万不要传 vector 的值!!!亲测会慢 10 倍!!!
代码
#include <map>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 1e5 +