对于树上问题我们一般还可以采取长链刨分进行树上问题的优化。
长链刨分就是以深度划分重儿子和轻儿子。
存在几个性质: 1. 所有链长度的和为O(n)级别的。 2. 任意一个点的k次祖先y所在的长链的长度大于等于k 3. 任何一个点向上跳跃重链的次数不超过$sqrt{n}$ 证明3:一个点从一个重链上调到另一个重链上 重链的长度分部的最坏情况是1 2 3 4...$sqrt{n}$所以最多跳$sqrt{n}$次。
应用:
- O(nlogn)-O(1)求K级祖先。
对于询问远大于点数求K级祖先时我们使用倍增可能会超时 但是我们进行倍增的预处理则不会 套上长链刨分我们即可O(1)回答。
具体实现:长链刨分过后我们对于每条长链维护该链长的祖先和儿子。我们利用倍增数组跳K的二进制位的最高位那一项 然后在跳到的点的长链顶点处求出。
所以 预处理nlogn 回答O(1).这样做的时间复杂度 每个点处预处理是链长之和所以为O(n),倍增预处理nlogn
考虑空间复杂度 显然也是O(n).
```
const int MAXN=500010;
int n,q,rt,len,last;
ui s;char ch;
ll ans;
int f[MAXN][21],Log[MAXN],son[MAXN],mx[MAXN],d[MAXN],top[MAXN];
int lin[MAXN],ver[MAXN],nex[MAXN];
//mx[x]表示以x点向下的最大深度 son[x]表示重儿子.
//top[x]表示以x这条链上的祖先.
vector 类似于dsu on tree 长链刨分过后每次不重新计算 全部继承重儿子的值对于轻儿子再暴力进行合并。
例题:[luogu3565](https://www.luogu.com.cn/problem/P3565)
给定一颗树在树上选择三个点要求两两距离相等 求方案数。
不难想到我们先求出两个点的距离相等的方案数再找第三个点即可。
f[i][j]表示以i为根的子树内距i距离为j的点的个数。
我们在每个LCA处合并答案,先求出点对数再求答案最后别忘了还有父亲方面的累加。
但是这样我们需要先求出f数组 然后再进行类似容斥的dp..(自己yy的一个想法。
考虑不这样做我们直接以某个点为中心统计答案n^2 但是这和dp没什么关系了。
考虑这样做我么统计答案的时候并不在三个点共有的中心统计 而是在最上端的那个点统计。
具体考虑我们g[i][j]表示多少点对的LCA到i距离为d[x]-j 之所以是这个状态 是因为也只能是这个状态了还是可以推出来的。
不过转移有点繁琐 学到了一个新的技巧观察转移方程式...不好描述...
具体转移如下:
```
inline void dfs(int x,int father)
{
d[x]=d[father]+1;f[x][0]=1;
son[x]=x;
for(int i=lin[x];i;i=nex[i])
{
int tn=ver[i];
if(tn==father)continue;
dfs(tn,x);
if(d[son[tn]]>d[son[x]])son[x]=son[tn];
for(int j=d[son[tn]];j>=0;--j)
{
if(j>=1)ans+=g[x][j]*f[tn][j-1];
ans+=f[x][j]*g[tn][j+1];
g[x][j]+=g[tn][j+1];
g[x][j+1]+=f[tn][j]*f[x][j+1];
f[x][j+1]+=f[tn][j];
}
}
}
```
当然这个dp还能优化。。
当然 我只是理解长链刨分的优化但是我不会指针分配的写法。
所以就咕咕咕了。
放两道例题:[codeforces1009F](https://www.luogu.com.cn/problem/CF1009F)
这道题可以写dsu 也可以线段树 当然还可以长链刨分优化dp了。最后咕咕咕。
[cogs2652]秘术「天文密葬法」 这个分数规划+长链刨分的题目 题目地址找了但是打不开。。
LINK:[攻略bzoj3252](http://www.lydsy.com/JudgeOnline/problem.php?id=3252)
题意:树版的k方格取数 贪心挺显然的,我们每次选取一条权值最大的路径将其清0.
为什么这样做是对的 其实可以考虑模拟费用流的思想我们这其实就是在模拟费用流。
这样做是$n^2$的 如何优化?考虑长链刨分。我们把权值改为链长 那么每次我们选取最长的重链.
其他链不受我们的影响 所以取前k大的重链即可。