算法问题描述
在树上的节点间的路径上进行各种操作
算法思想
对于序列区间,我们有强有力的工具——线段树,但对于树这种结构本身并不是一长条链,但我们可以人为剖分出一条一条链来进行区间维护,最后合并这一条条链就得到树上区间的答案了。接下来的问题是如何剖分,显然对于随机数据我们随机剖分的复杂度应该是满足logn的性质的,但通常数据会可以编造,所以我们需要启发式剖分。
启发式剖分
每个节点向自己孩子最多的子节点连重边其他连轻边,最后图中一定有多条重边连成的重链,重链在线段树中放在一起做区间处理,其他的按深搜序放,这样的好处的一个节点的子树在线段树上就是连续的了,可以附带支持子树操作
复杂度口糊
考虑每个非叶子节点只会有一个重边,重边是能加速操作的,而那些轻边只能单独存在地操作,所以轻边越多复杂度越高,但是每个节点轻边多意味着每个节点的孩子多,意味着整棵树的层数小,树上路径短,所以综合想象一下这样剖分的复杂度还是挺O98K的
代码
work是各种操作,如query,update等
void build(int o,int f,int r){
tr[o]=(TREE){f,r,++tot};
if(f==r){
tr[o].w=w[rnk[f]];
return;
}
build(o<<1,f,mid);
build(o<<1|1,mid+1,r);
pushup(o);
}
void work(int x,int y){return;}//略
void DFS1(int u){
size[u]=1;
dep[u]=dep[fa[u]]+1;
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
if(dep[v]) continue;
fa[v]=u;
DFS1(v);
size[u]+=size[v];
if(size[v]>size[son[u]] || !son[u]) son[u]=v;
}
}
void DFS2(int u,int tp){
top[u]=tp;
tid[u]=++tim;
rnk[tim]=u;
if(!son[u]) return ;
DFS2(son[u],tp);
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
if(v==fa[u] || v==son[u]) continue;
DFS2(v,v);
}
}
void work_path(int x,int y){
int fx=top[x],fy=top[y];
while(fx!=fy){
if(dep[fx]<dep[fy]) swap(x,y),swap(fx,fy);
work(1,tid[fx],tid[x]);
x=fa[fx];
fx=top[x];
}
if(dep[x]>dep[y]) swap(x,y);
work(1,tid[x],tid[y]);
}