首先看一下树链剖分是什么东西:
当我们要同时解决以下两个问题时:
1.求树上两点简单路径上的最大最小值(和)
2.修改树上两点简单路径上的边权(点权)
如果只用单独考虑,那么第一个就是树上差分,第二个是倍增,可是如果同时要有两个操作,那么时间复杂度就会大幅度退化,所以现在要用一个新的方法:树链剖分
它将一棵树剖成了几条链来组成,且每个点最多只会存在一条链上,且只出现一次。那么怎么进行剖分呢?
首先定义一些东西:
重儿子:父亲结点的所有儿子结点中,子树拥有结点数最多的结点。
轻儿子:父亲结点除了重儿子以外的所有儿子。
重边:父亲结点与重儿子的连边。
轻边:父亲结点与轻儿子的连边。
重链:所有重边所组成的一条链。
就是这个样子,粗的边就是重边,不过链的长度可以为0,就是只有一个点
在进行树链剖分时,我们将进行这些变量声明:
f[u]:保存点u的父亲结点
d[u]:保存点u的深度
size[u]:保存以u为根节点的子树的结点个数
son[u]:保存点u的重儿子
top[u]:保存点u所在重链的顶端结点
id[u]:保存点u进行重新编号后的新的编号
然后就可以开始搞了:
inline void dfs( int x , int f ){
sz[x] = 1;
fa[x] = f;
for( register int i = 0 ; i < G[x].size() ; i ++ ){
int v = G[x][i];
if( v == f ) continue;
depth[v] = depth[x] + 1;//求深度
dfs( v , x );
sz[x] += sz[v];//确定son[x],用sz[v]较大的v坐
if( sz[son[x]] < sz[v] )
son[x] = v;
}
}
inline void dfs2( int x , int f ){
top[x] = f;//这条链的最高点
id[x] = ++cnt;//进行编号
if( !son[x] )
return ;
dfs2( son[x] , f );//先找重边,再找轻边
for( register int i = 0 ; i < G[x].size() ; i ++ ){
int v = G[x][i];
if( id[v] ) continue;
dfs2( v ,v );
}
}
void get_ans( int x ){
int fx = top[x] , y = 1 , fy = top[1];//x,y的简单路径上的查询,这里y=1
int sum = 0;
while( fx != fy ){
if( depth[fx] >= depth[fy] ){
ans = 0;
query( 1 , id[fx] , id[x] );//注意谁的编号大
sum += ans;
insert_( 1 , id[fx] , id[x] , 2 );
x = fa[fx];fx = top[x];
}
else{
ans = 0;
query( 1 , id[fy] , id[y] );
sum += ans;
insert_( 1 , id[fy] , id[y] , 2 );
y = fa[fy];fy = top[y];
}
}//在最后一个链内,还需要判断它们内部的距离
if( depth[x] <= depth[y] ){
ans = 0;
query( 1 , id[x] , id[y] );
sum += ans;
insert_( 1 , id[x] , id[y] , 2 );
}
else{
ans = 0;
query( 1 , id[y] , id[x] );
sum += ans;
insert_( 1 , id[y] , id[x] , 2 );
}
printf( "%d\n" , sum );
}
先来一个板题:软件包管理器
我们要知道一个性质,对于一个点x,它的子树编号一定是比他大的,这样对于子树查找与修改就很简单了
然后区间修改就用线段树即可
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <algorithm>
#define ll long long
using namespace std;
const int MAXN = 100013;
int n;
struct node{
int l , r;
int lazy , sum;
}tre[MAXN*4];
int sz[MAXN] , son[MAXN] , top[MAXN] , depth[MAXN] , id[MAXN] , fa[MAXN] , cnt;
vector<int> G[MAXN];
inline void dfs( int x , int f ){
sz[x] = 1;
fa[x] = f;
for( register int i = 0 ; i < G[x].size() ; i ++ ){
int v = G[x][i];
if( v == f ) continue;
depth[v] = depth[x] + 1;
dfs( v , x );
sz[x] += sz[v];
if( sz[son[x]] < sz[v] )
son[x] = v;
}
}
inline void dfs2( int x , int f ){
top[x] = f;
id[x] = ++cnt;
if( !son[x] )
return ;
dfs2( son[x] , f );
for( register int i = 0 ; i < G[x].size() ; i ++ ){
int v = G[x][i];
if( id[v] ) continue;
dfs2( v ,v );
}
}
inline void build( int i , int l , int r ){
tre[i].l = l , tre[i].r = r;
tre[i].sum = tre[i].r - tre[