DFS 序 1(小树剖)
题目来源:https://vjudge.net/contest/642764#problem/H
//
题意:给你一棵树,树上的每一个节点都有自己的权值,现在有两种操作:op1:在某一个节点的权值增加x。op2:求a节点子树所有的权值之和。
//
算法介绍:
树剖:将树剖解为一连串的链式结构,并对所有节点重新编号,链式结构中的节点新编号都是连续的,一个节点子树的所有编号也都是连续的。 能对树上某两个节点的dfs路径上的权值增改,查询(涉及到跳点)。某个节点子树节点的权值增改,查询。把树转为链,通过线段树区间维护。
//
思路:这个题仅仅是对子树操作(阉割了跳点操作),在dfs中,仅仅只需要把树节点重新编为连续号,用dfn[i]记录i节点的新编号,用over[i]记录i节点子树的最后节点的新编号。
一棵树dfs序新编号好后就是:

//
可以看到,树剖后,所有的子树编号都是按照dfs序的连续编号,绿色圈中,例如对8节点的子树操作,子树结尾编号就是over[8]=12。然后讲新编号放入线段树里维护就行了。
//
题解:
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define lp p<<1
#define rp p<<1|1
using namespace std;
const int N=1e6+9;
int n,q,R,op,tot=0,a,x;
struct Nod{
int sum,add;
}tree[N<<2];
vector<int>arr(N),dfn(N),over(N),num(N);
vector<int>G[N];
//dfn:新编号,
//over:i节点dfs序的末节点
//num:新旧节点映射
void dfs(int u,int fa){//小数剖:对树节点重新编号
dfn[u]=++tot,num[tot]=u;
for(auto v:G[u]){
if(v==fa) { continue;}
dfs(v,u);
}
over[u]=tot;
}
void pushup(int p){
tree[p].sum=tree[lp].sum+tree[rp].sum;
}
void pushdown(int p,int len){
if(tree[p].add){
tree[lp].sum+=(len-(len>>1))*tree[p].add,tree[rp].sum+=(len>>1)*tree[p].add;
tree[lp].add+=tree[p].add,tree[rp].add+=tree[p].add;
tree[p].add=0;
}
}
void build(int s,int t,int p){
if(s==t){
tree[p].sum=arr[num[s]];//权值还是原来编号记录的,要映射回去
return;
}
int m=(s+t)>>1;
build(s,m,lp),build(m+1,t,rp);
pushup(p);
}
void update(int l,int r,int k,int s,int t,int p){
if(l<=s && r>=t){
tree[p].sum+=(t-s+1)*k;
tree[p].add+=k;
return;
}
pushdown(p,t-s+1);
int m=(s+t)>>1;
if(l<=m) { update(l,r,k,s,m,lp);}
if(r>=m+1) {update(l,r,k,m+1,t,rp);}
pushup(p);
}
int query(int l,int r,int s,int t,int p){
if(l<=s && r>=t){
return tree[p].sum;
}
pushdown(p,t-s+1);
int m=(s+t)>>1,sum=0;
if(l<=m) {sum=query(l,r,s,m,lp);}
if(r>=m+1) {sum+=query(l,r,m+1,t,rp);}
pushup(p);
return sum;
}
signed main()
{
cin>>n>>q>>R;
for(int i=1;i<=n;i++){cin>>arr[i];}
int u,v;
for(int i=1;i<n;i++){
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs(R,R);
build(1,n,1);
while(q--){
cin>>op;
if(op==1){//a节点权值+x
cin>>a>>x;
update(dfn[a],dfn[a],x,1,n,1);
}
else if(op==2){//查询a的子树所有权值和
cin>>a;
cout<<query(dfn[a],over[a],1,n,1)<<'\n';
}
}
return 0;
}
浙公网安备 33010602011771号