有关树链剖分

数据结构,是一种以巧妙存储方式和相应的数据处理规则优化数据处理速度的工具;

树链剖分:一种处理树上链的相关信息的方法

               ——博主语;

今天将树链剖分啊,树链剖分好神啊!!

从前对她的印象:

“这题你用的什么?”某高三金牌学长

“树链剖分呀”某高二学长

“胡说,树链剖分是NOIP算法么,你要这样这样做...才像是NOIP选手啊”高三学长

。。。。。。

 

思路和实现:

对一棵树我们想很快的查询两点间的权值,怎么办呢?

LCA再记点到根的距离即可;

如果,不止是这些,我们还想对她的某点甚至某链进行修改呢?

这怎么行,你当是线段树么!!

其实是可以的,我们今天讲链剖,她就是干这个的。

我们知道线段树在线段上满足我们的需求;

于是我们试着把树简单的展成一个线段——用DFS序,然后维护线段树,线段树上的叶节点原树上点一一对应

然后发现不行。。。

DFS序中两点间的点并非她们路径;

然而,对某些点而言,DFS序中间隔的点就她们路径(你应该明白);

(dfs序:指在dfs时各点被第一次到达的顺序)

基于这个关系;

我们可以在原来的线段树维护dfs序的设想上,添加“把任意链分解可在DFS序线段树中查询的链,然后分别查询”的思路;

这也就是树链剖分啦!!

前提是我们得记录在dfs序中以点v为止最长区间(记作X(v)),她满足是同一条链;

但dfs序是不定的,所以我们还得规定一种方便的dfs方式,最好还得高效

这里引入轻重链的概念;

  • 重边:对每个节点,她向子树最多的子节点连的边为重边,叶节点向自己连一条重边;
  • 重链:连续的重边构成一条重链;
  • 轻链(边):重边之外的边;

可以看出:

  1. 不存在不属于重链的点;
  2. 不存在连续的轻边;
  3. 每个点向下只连一条重边;

这样的话,我们可以维护使每条重链连续的dfs序,方法是dfs时对每个点优先跑重边

可以第一遍dfs跑出深度dep(x),子树大小size(x),重边hline(x);

 1 void dfs_1(int now){
 2     int j=first[now];
 3     while(j){
 4         if(!dep[x[j].to]){
 5             dep[x[j].to]=dep[now]+1;
 6             fa[x[j].to]=now;
 7             dfs_1(x[j].to);
 8             size[now]+=size[x[j].to];
 9             if(hine[now]==now||size[x[j].to]>size[hine[now]])
10                 hine[now]=x[j].to;
11         }
12         j=x[j].next;
13     }
14     size[now]++;
15 }

第二遍dfs跑出个点在线段树的线段中的位置rank(x),和线段中b位置对应树上x点a(b)=x——也是dfs序;(这里点一下rank和a的关系)

 1 void dfs_2(int now,int top_now){
 2     int j=first[now];
 3     top[now]=top_now;
 4     a[++num]=now;
 5     rank[now]=num;
 6     if(hine[now]!=now)
 7         dfs_2(hine[now],top_now);
 8     while(j){
 9         if(dep[x[j].to]==dep[now]+1&&x[j].to!=hine[now])
10             dfs_2(x[j].to,x[j].to);
11         j=x[j].next;
12     }
13 }

然后是查询的问题,

设top(x)为x所属重链的顶节点,dep(x)为x的深度,fa(x)为x的父节点

对于(u,v):

while(top(u)!=top(v))

  if(dep(top(u))dep(top(v)))

    find(top(u),u)

    u=fa(top(u))

  else

    find(top(v),v)

    v=fa(top(v))

if(u!=v)//只有在维护边,并把边权加在点上时,有这个限制

  find(u,v)

每次,跳u,v两条重链中端点深度大的重链,然后把uorv指针转到重链端点父亲的重链上,重复该过程

效率:

为什么这样剖分快呢?

性质:

  1. 若(u,v)为轻边,则size(v)<size(u)/2

  2. 从某一点到根的路径上轻边的个数不大于O(logn)

  3. 从某一点到根的路径上重链不会超过O(logn)

性质一出自对重边的定义:若size(v)≥size(u)/2,她必然是一条重边

性质二由性质一推得:由于size(v)<size(u)/2,故size(u)>2size(v),若点x到根的路径上轻边的个数大于O(logn),则size(root)>size(x)*2^logn>n;————这显然是不可能的;

性质三由性质二推得:每一条轻边分割出一条重链;

所以,这个树链剖分基于线段树,有了O(m*(log^2 n))的效率

 例题:

BZOJ P2157

BZOJ P2157 题解

洛谷 p3384

洛谷 P3384 题解

原文地址:https://www.cnblogs.com/nietzsche-oier/p/6384015.html