题意:一棵n个节点的树,每个点都有一个权值w,三种操作:
1、CHANGE x y 把x节点的权值改为y
2、QMAX x y 询问x到y路径上节点的最大权值
3、QSUM x y 询问x到y路径的节点和,包括x和y本身
树链剖分裸模板..
简单介绍下树链剖分
一般用来维护两个点之间路径上各点(边)权值的最大、最小,权值和并且支持在线更改任意节点(边)的权值(把链剖出来挂在线段树上)
明确一些定义:
size:size[i]为以该点为根节点的子树的节点数。
重儿子:重儿子为该点的儿子中size最大的(有多个最大时任选一个。),一个非叶子节点有且只有一个重儿子
重链:由根节点开始,每个点每次都访问自己的重儿子,一直访问到叶子节点,就组成了一条重链。对于一个点的非重儿子来说,以他为根节点,可以重新访问出一条重链
top:重链中深度最小的节点
链的深度:定义根节点的深度为1,一条重链的深度为father[top]所在重链的深度+1
显然,一棵树就被剖成了若干条链互不重合的链
用两次dfs维护
第一遍(dfs):得到size和每个点重儿子
第二遍(make):形成若干条重链,并得到相应的top和深度,因为要挂在线段树中维护,所以每个点按照dfs的顺序得到一个在线段树中对应的编号
剩下的交给线段树维护即可
对于两个点的询问,如果在同一条链上,线段数求出答案,如果不在同一条链上,先把他们升到同一条链上(类似lca的方法,但lca用的是倍增,而这里是每次处理一条链)同时更新答案
对于修改,要注意修改的是x节点在线段树中的位置,所以修改的时候上传的编号是在线段树中的该节点编号
type
rec=record
l,r,max,sum:longint;
end;
var
n,l,m,x,y,tot :longint;
i :longint;
ch :char;
s :string;
vis :array[0..30010] of boolean;
last,w,father,a,num :array[0..30010] of longint;
size,top,d,max_son :array[0..30010] of longint;
pre,other :array[0..60010] of longint;
t :array[0..90010] of rec;
function max(a,b:longint):longint;
begin
if a>1;
build(2*x,l,mid); build(2*x+1,mid+1,r);
t[x].max:=max(t[x*2].max,t[2*x+1].max);
t[x].sum:=t[2*x].sum+t[2*x+1].sum;
end;
function get_max(x,l,r:longint):longint;
var
mid:longint;
begin
if (t[x].l=l) and (t[x].r=r) then exit(t[x].max);
mid:=(t[x].l+t[x].r) >>1;
if (r<=mid) then exit(get_max(2*x,l,r)) else
if (l>mid) then exit(get_max(2*x+1,l,r)) else
exit(max(get_max(2*x,l,mid),get_max(2*x+1,mid+1,r)));
end;
function get_sum(x,l,r:longint):longint;
var
mid:longint;
begin
if (t[x].l=l) and (t[x].r=r) then exit(t[x].sum);
mid:=(t[x].l+t[x].r)>>1;
if (r<=mid) then exit(get_sum(2*x,l,r)) else
if (l>mid) then exit(get_sum(2*x+1,l,r)) else
exit(get_sum(2*x,l,mid)+get_sum(2*x+1,mid+1,r));
end;
procedure change(x,y,z:longint);
var
mid:longint;
begin
if (t[x].l=y) and (t[x].r=y) then
begin
t[x].sum:=z;
t[x].max:=z;
exit;
end;
mid:=(t[x].l+t[x].r)>>1;
if (y<=mid) then change(2*x,y,z) else change(2*x+1,y,z);
t[x].max:=max(t[2*x].max,t[2*x+1].max);
t[x].sum:=t[2*x].sum+t[2*x+1].sum;
end;
procedure dfs(x:longint);
var
p,q:longint;
begin
size[x]:=1;
q:=last[x];
while (q<>0) do
begin
p:=other[q];
if not vis[p] then
begin
vis[p]:=true;
father[p]:=x;
dfs(p);
inc(size[x],size[p]);
if size[max_son[x]]0 then
begin
vis[max_son[x]]:=true;
make(max_son[x],t,depth);
end;
q:=last[x];
while (q<>0) do
begin
p:=other[q];
if not vis[p] and (p<>max_son[x]) then
begin
vis[p]:=true;
make(p,p,depth+1);
end;
q:=pre[q];
end;
end;
procedure ask_max(x,y:longint);
var
ans:longint;
begin
ans:=-maxlongint;
if d[x]>d[y] then swap(x,y);
while (d[x]top[y]) do
begin
ans:=max(ans,get_max(1,num[top[x]],num[x]));
ans:=max(ans,get_max(1,num[top[y]],num[y]));
x:=father[top[x]];
y:=father[top[y]];
end;
if num[x]>num[y] then ans:=max(ans,get_max(1,num[y],num[x])) else ans:=max(ans,get_max(1,num[x],num[y]));
writeln(ans);
end;
procedure ask_sum(x,y:longint);
var
ans:longint;
begin
ans:=0;
if d[x]>d[y] then swap(x,y);
while (d[x]top[y]) do
begin
inc(ans,get_sum(1,num[top[x]],num[x]));
inc(ans,get_sum(1,num[top[y]],num[y]));
x:=father[top[x]];
y:=father[top[y]];
end;
if num[x]>num[y] then inc(ans,get_sum(1,num[y],num[x])) else inc(ans,get_sum(1,num[x],num[y]));
writeln(ans);
end;
begin
read(n);
for i:=1 to n-1 do
begin
read(x,y);
connect(x,y);
connect(y,x);
end;
for i:=1 to n do read(w[i]);
vis[1]:=true; dfs(1);
fillchar(vis,sizeof(vis),false);
vis[1]:=true; make(1,1,1);
build(1,1,n);
readln(m);
for i:=1 to m do
begin
s:='';
read(ch);
while (ch<>' ') do
begin
s:=s+ch;
read(ch);
end;
if s='CHANGE' then
begin
readln(x,y);
change(1,num[x],y);
end else
if s='QMAX' then
begin
readln(x,y);
ask_max(x,y);
end else
begin
readln(x,y);
ask_sum(x,y);
end;
end;
end.
——by Eirlys