思路
初看题目,这题应该是一个线段树
但是,这题的存储结构怎么是树啊啊啊
于是,我们可能会想到把树分解成若干条链,在进行操作,这就是题目中说的树链剖分
树链剖分
常用于解决静态树上路径信息。
核心:如何恰当地将树剖分为若干条链。
常见剖分方式 :
- 重链剖分
- 长链剖分
- 虚链剖分
这里我们只介绍重链剖分
对树的边进行分类
- 重边
- 轻边
以任意点为根,记
size(u)
以
u
为根节点的子树的结点总数。令
v
是
u
的所有
儿子节点中
size
值最大的,
u-v
边称为重边,其他就是轻边。
由重边构成的链,称为重链。单个的视为长度为
1
的。
性质
- 如果 (u,v) 为轻边,那么 size(v) ≤ size(u)/2
- 从根到某个节点v的路径上的轻边个数不多于 log n
- 当某个链全部由重链组成。对于每个结点到根的路径上都有不超过 log n 条轻边和 log n 条重路径
整体复杂度
预处理
两次
DFS
DFS1 :
建树
DFS2:
剖分
第
1
次
DFS
中需要维护
- dep[u] 深度
- fa[u] 父节点信息
- siz[u] 以u为根节点的子树的总结点数
- son[u] : u的重儿子
代码如下:
void dfs1(int u,int f)
{
dep[u]=dep[f]+1;
fa[u]=f;
siz[u]=1;
for(auto v:G[u])
{
if(v==f) continue;
dfs1(v,u);
siz[u]+=siz[v];
if(!son[u] ||siz[son[u]]<siz[v])
{
son[u]=v;
}
}
}
第
2
次
DFS
-
top[u] u 所在重链上的顶端结点编号
-
id[u] u 在结点序列中的位置下标,也就是时间戳(id数组在一些简单题里可以不用)
代码如下:
void dfs2(int u,int topu)
{
top[u]=topu;
id[u]=++dfn;
a[dfn]=ori[u];
if(!son[u]) return ;
dfs2(son[u],topu);
for(auto v:G[u])
{
if(v==fa[u] || v==son[u]) continue;
dfs2(v,v);
}
}
这时我们注意到,我们所要处理的所有区间均为连续编号,于是想到线段树,用线段树处理连续编号区间和
代码如下:
const int N=1e5+5;
int n,m;
int ori[N],a[N];
vector<int> G[N];
int w[N<<2],lzy[N<<2];
void pushup(int u){
w[u]=w[2*u]+w[2*u+1];
}
void build(int u,int L,int R){
lzy[u]=0;
if(L==R){
w[u]=a[L];
return ;
}
int mid=(L+R)>>1;
build(2*u,L,mid);
build(2*u+1,mid+1,R);
pushup(u);
}
bool inRange(int l,int r,int L,int R){
return (l<=L)&&(R<=r);
}
bool outofRange(int l,int r,int L,int R){
return r<L||R<l;
}
void makeTag(int u,int len,int x){
lzy[u]+=x;
w[u]+=len*x;
}
void pushdown(int u,int l,int r){
int mid=(l+r)/2;
makeTag(2*u,mid-l+1,lzy[u]);
makeTag(2*u+1,r-mid,lzy[u]);
lzy[u]=0;
}
void update(int u,int l,int r,int L,int R,int x){
if(inRange(L,R,l,r)){
makeTag(u,r-l+1,x);
}else if(!outofRange(L,R,l,r)){
int mid=(l+r)/2;
pushdown(u,l,r);
update(2*u,l,mid,L,R,x);
update(2*u+1,mid+1,r,L,R,x);
pushup(u);
}
}
int query(int u,int l,int r,int L,int R){
if(inRange(L,R,l,r)){
return w[u];
}else if(!outofRange(L,R,l,r)){
int mid=(l+r)/2;
pushdown(u,l,r);
return (query(2*u,l,mid,L,R)+query(2*u+1,mid+1,r,L,R));
}else{
return 0;
}
}
int dep[N],fa[N],siz[N],son[N],top[N],id[N];
int dfn;
void dfs1(int u,int f){
dep[u]=dep[f]+1;
fa[u]=f;
siz[u]=1;
for(auto v:G[u]){
if(v==f) continue;
dfs1(v,u);
siz[u]+=siz[v];
if(!son[u] ||siz[son[u]]<siz[v]){
son[u]=v;
}
}
}
void dfs2(int u,int topu){
top[u]=topu;
id[u]=++dfn;
a[dfn]=ori[u];
if(!son[u]) return ;
dfs2(son[u],topu);
for(auto v:G[u]){
if(v==fa[u] || v==son[u]) continue;
dfs2(v,v);
}
}
void updateRange(int x,int y,int z){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
update(1,1,n,id[top[x]],id[x],z);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
update(1,1,n,id[x],id[y],z);
}
int queryRange(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
ans+=query(1,1,n,id[top[x]],id[x]);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
ans+=query(1,1,n,id[x],id[y]);
return ans;
}
void updateTree(int x,int k){
update(1,1,n,id[x],id[x]+siz[x]-1,k);
}
int queryTree(int x){
return query(1,1,n,id[x],id[x]+siz[x]-1);
}
Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+5;
int n,m;
int ori[N],a[N];
vector<int> G[N];
int w[N<<2],lzy[N<<2];
void pushup(int u){
w[u]=w[2*u]+w[2*u+1];
}
void build(int u,int L,int R){
lzy[u]=0;
if(L==R){
w[u]=a[L];
return ;
}
int mid=(L+R)>>1;
build(2*u,L,mid);
build(2*u+1,mid+1,R);
pushup(u);
}
bool inRange(int l,int r,int L,int R){
return (l<=L)&&(R<=r);
}
bool outofRange(int l,int r,int L,int R){
return r<L||R<l;
}
void makeTag(int u,int len,int x){
lzy[u]+=x;
w[u]+=len*x;
}
void pushdown(int u,int l,int r){
int mid=(l+r)/2;
makeTag(2*u,mid-l+1,lzy[u]);
makeTag(2*u+1,r-mid,lzy[u]);
lzy[u]=0;
}
void update(int u,int l,int r,int L,int R,int x){
if(inRange(L,R,l,r)){
makeTag(u,r-l+1,x);
}else if(!outofRange(L,R,l,r)){
int mid=(l+r)/2;
pushdown(u,l,r);
update(2*u,l,mid,L,R,x);
update(2*u+1,mid+1,r,L,R,x);
pushup(u);
}
}
int query(int u,int l,int r,int L,int R){
if(inRange(L,R,l,r)){
return w[u];
}else if(!outofRange(L,R,l,r)){
int mid=(l+r)/2;
pushdown(u,l,r);
return (query(2*u,l,mid,L,R)+query(2*u+1,mid+1,r,L,R));
}else{
return 0;
}
}
int dep[N],fa[N],siz[N],son[N],top[N],id[N];
int dfn;
void dfs1(int u,int f){
dep[u]=dep[f]+1;
fa[u]=f;
siz[u]=1;
for(auto v:G[u]){
if(v==f) continue;
dfs1(v,u);
siz[u]+=siz[v];
if(!son[u] ||siz[son[u]]<siz[v]){
son[u]=v;
}
}
}
void dfs2(int u,int topu){
top[u]=topu;
id[u]=++dfn;
a[dfn]=ori[u];
if(!son[u]) return ;
dfs2(son[u],topu);
for(auto v:G[u]){
if(v==fa[u] || v==son[u]) continue;
dfs2(v,v);
}
}
void updateRange(int x,int y,int z){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
update(1,1,n,id[top[x]],id[x],z);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
update(1,1,n,id[x],id[y],z);
}
int queryRange(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
ans+=query(1,1,n,id[top[x]],id[x]);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
ans+=query(1,1,n,id[x],id[y]);
return ans;
}
void updateTree(int x,int k){
update(1,1,n,id[x],id[x]+siz[x]-1,k);
}
int queryTree(int x){
return query(1,1,n,id[x],id[x]+siz[x]-1);
}
signed main()
{
int x,y,z,op;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&ori[i]);
}
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
dfs1(1,0);
dfs2(1,1);
build(1,1,n);
while(m--){
scanf("%d",&op);
switch(op){
case 1:scanf("%d%d",&x,&z);
updateRange(x,x,z);
break;
case 2:scanf("%d%d",&x,&z);
updateTree(x,z);
break;
case 3:scanf("%d",&x);
printf("%d\n",queryRange(1,x));
}
}
return 0;
}