树链剖分
学习计划如下
1.线段树学好
2.dfs序学好
3.脑子修好
补坑
课件
应该很详细了吧。
总的来说,先把树剖分成轻边和重边,在修改路径权值时,不断靠到同一条重链上,最后用线段树等经济结构维护重链,将修改和查询从\(O(n)\)降到\(O(logN)\)。
轻边和重边一视同仁,全部加到线段树里面,轻边被视为很小的重边,复杂度均摊为\(O(log(N)\)
用途,维护树的信息。
1:最短路树割掉一些边的次短路。
其他的以后碰到再填吧

代码老惯例,等能随便敲出来再贴吧。
模板敲出来啦~~
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstdlib>
#include<stack>
using namespace std;
const int maxn = 400000;
struct Edge{int to,next;}e[maxn];
struct seg{int l,r,sum,mx;}t[maxn];
int fa[maxn],bl[maxn],dep[maxn],sz[maxn],v[maxn],head[maxn],pos[maxn];
int cnt = 0,tot;
void insert(int u,int v)
{
e[++cnt].to = v;e[cnt].next = head[u];head[u] = cnt;
e[++cnt].to = u;e[cnt].next = head[v];head[v] = cnt;
}
int N,M,R,P;
void gi(int &x){
x = 0;int f = 1;char c = getchar();
while(c < '0'||c > '9'){if(c == '-')f = -1;c = getchar();}
while('0' <= c&&c <= '9') x = x*10+c-'0',c = getchar();
x *= f;
}
void init()
{
gi(N);
for(int i = 1;i < N;i++){
int u,v;
gi(u);gi(v);
insert(u,v);
}
for(int i = 1;i <= N;i++) gi(v[i]);
}
void dfs1(int x)
{
sz[x] = 1;
for(int i = head[x];i;i = e[i].next)
{
if(e[i].to == fa[x]) continue;
dep[e[i].to] = dep[x]+1;
fa[e[i].to] = x;
dfs1(e[i].to);
sz[x] += sz[e[i].to];
}
}
void dfs2(int x,int chain)
{
int k = 0;tot++;
bl[x] = chain;
pos[x] = tot;
for(int i = head[x];i;i = e[i].next)
if(dep[e[i].to] > dep[x]&&sz[e[i].to] > sz[k])
k = e[i].to;
if(k == 0) return;
dfs2(k,chain);
for(int i = head[x];i;i = e[i].next)
if(dep[e[i].to] > dep[x]&&k != e[i].to)
dfs2(e[i].to,e[i].to);
}
void build(int o,int l,int r)
{
t[o].l = l;t[o].r = r;
if(l == r) return;
int mid = l+(r-l)/2;
build(o*2,l,mid);
build(o*2+1,mid+1,r);
}
void change(int o,int x,int v)
{
int l = t[o].l,r = t[o].r;
int mid = l+(r-l)/2;
if(l == r){t[o].sum = t[o].mx = v;return;}
if(x <= mid) change(o*2,x,v);
else change(o*2+1,x,v);
t[o].sum = t[o*2].sum+t[o*2+1].sum;
t[o].mx = max(t[o*2].mx,t[o*2+1].mx);
}
int querysum(int o,int x,int y)
{
int l = t[o].l,r = t[o].r;
int mid = l+(r-l)/2;
if(l == x&&r == y) return t[o].sum;
if(y <= mid) return querysum(o*2,x,y);
else if(x > mid) return querysum(o*2+1,x,y);
else return querysum(o*2,x,mid)+querysum(o*2+1,mid+1,y);
}
int querymx(int o,int x,int y)
{
int l = t[o].l,r = t[o].r;
int mid = l+(r-l)/2;
if(l == x&&r == y) return t[o].mx;
if(y <= mid) return querymx(o*2,x,y);
else if(x > mid) return querymx(o*2+1,x,y);
else return max(querymx(o*2,x,mid),querymx(o*2+1,mid+1,y));
}
int solvesum(int x,int y)
{
int sum = 0;
while(bl[x] !=bl[y])
{
if(dep[bl[x]] < dep[bl[y]]) swap(x,y);
sum += querysum(1,pos[bl[x]],pos[x]);
x = fa[bl[x]];
}
if(pos[x] > pos[y]) swap(x,y);
sum += querysum(1,pos[x],pos[y]);
return sum;
}
const int INF = ~0U>>1;
int solvemx(int x,int y)
{
int M = -INF;
while(bl[x] != bl[y]){
if(dep[bl[x]] < dep[bl[y]]) swap(x,y);
M = max(M,querymx(1,pos[bl[x]],pos[x]));
x = fa[bl[x]];
}
if(pos[x] > pos[y]) swap(x,y);
M = max(M,querymx(1,pos[x],pos[y]));
return M;
}
void buildseg()
{
build(1,1,N);
for(int i = 1;i <= N;i++)
change(1,pos[i],v[i]);
}
void solve()
{
char s[10];
gi(M);int x,y;
for(int i = 0;i < M;i++)
{
scanf("%s",s);
gi(x),gi(y);
if(s[1] == 'M'){
printf("%d\n",solvemx(x,y));
}else if(s[1] == 'S'){
printf("%d\n",solvesum(x,y));
}else if(s[1] == 'H'){
v[x] = y;
change(1,pos[x],y);
}
}
}
int main()
{
init();
dfs1(1);
dfs2(1,1);
buildseg();
solve();
return 0;
}
虽然应该还能优化。不过也还不错啦。
谢谢hzwer的博客
注意:
修改线段树的值,不是节点的标号,板子里是修改\(pos[x]\)而不是\(x\)
好像没啦
完全没有想象中那么高大上啊。

浙公网安备 33010602011771号