写在前面:树链剖分是一种高效的数据结构,它的作用是将一棵点权树(一定要是点权树)拆分成链,然后用各种方法来高效解决问题。
树链剖分就是以一种方式遍历树,将原来的树该成一条链,,然后对这条链进行各种高效的操作。
这种方式就是优先遍历最大的子树,我们还是严格的从定义学起。
定义:
重儿子:siz[u]为v的子节点中siz值最大的,那么u就是v的重儿子。
轻重边有几个性质:
性质 1:如果(u, v) 为轻边,则 size(v) size(u)/ 2 。
证明:设 size(v) size(u)/ 2 ,则 size(v) 必然比其他儿子的 size 要大,那么(u, v)必然为重边,与(u, v) 为轻边矛盾。
性质 2:从根到某一点 V 的路径上的轻边个数不大于 O(log n) 。
证明:V 为叶子节点时轻边个数最多。
由性质 1 可知,每经过一条轻边,子树的节点个数至少比原来少一半,所以至多经过 O(log n) 条轻边就到达叶子节点了。
性质 3:我们称某条路径为重路径(链),当且仅当它全部由重边组成。那么对于每个点到根的路径上都不超过 O(log n) 条轻边和 O(log n) 条重路径。证明:显然每条重路径的起点和终点都是由轻边构成,而由性质 2 可知,每个点到根节点的轻边个数为 O(log n) ,所以重路径个数也为 O(log n) 。
同时我们也容易发现,一个点在且只在一条重路径上,而每条重路径一定是一条从根结点方向向叶结点方向延伸的深度递增的路径
在这里,当然有很多数组,现在我来分别介绍它们的作用:
top[]数组,用来保存当前节点的所在链的顶端节点
son[]数组,用来保存重儿子
dep[]数组,用来保存当前节点的深度
fa[]数组,用来保存当前节点的父亲
tid[]数组,用来保存树中每个节点剖分后的新编号
siz[]数组,用来保存以x为根的子树节点个数
rank[]数组,用来保存当前节点在线段树中的位置
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=300000;
struct tree{int l,r,v,lazy;}node[2*maxn];
int first[maxn],last[maxn],nxt[maxn],a[maxn],w[maxn],tid[maxn],e[maxn],top[maxn],rank[maxn],size[maxn],r[maxn],tt[maxn],father[maxn],n,m,k,frog,x,y,g,i,s,tot,z;
void add(int x,int y){a[++k]=y;tt[x]++;if(first[x]==0)first[x]=k;else nxt[last[x]]=k;last[x]=k;}
void dfs(int u,int fa)
{
int i=0;size[u]=1;r[u]=r[fa]+1;
father[u]=fa;
for(i=first[u];i;i=nxt[i])
if(a[i]!=fa)
{
dfs(a[i],u);
size[u]+=size[a[i]];
}
}
void dfs2(int u,int head)
{
int i,mson=0;
top[u]=head;tid[u]=++tot;rank[tid[u]]=tot;change(1,tot,tot,w[u]);
if(tt[u]==1&&u!=s)
{
e[u]=u;
return;
}
for(i=first[u];i>0;i=nxt[i])
if(size[a[i]]<size[u]&&size[a[i]]>size[mson])mson=a[i];
dfs2(mson,head);
e[u]=e[mson];
for(i=first[u];i>0;i=nxt[i])
if(size[a[i]]<size[u]&&a[i]!=mson){dfs2(a[i],a[i]);e[u]=e[a[i]];}
}
int main()
{
return 0;
}