Description
有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个
操作,分为三种:
操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
Input
第一行包含两个整数 N M 。表示点数和操作数。
接下来一行 N 个整数,表示树中节点的初始权值。
接下来 N-1 行每行三个正整数 fr to , 表示该树中存在一条边 (fr to) 。
再接下来 M 行,每行分别表示一次操作。其中第一个数表示该操
作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。
output
对于每个询问操作,输出该询问的答案。答案之间用换行隔开。
Sample input
5 5 1 2 3 4 5 1 2 1 4 2 3 2 5 3 3 1 2 1 3 5 2 1 2 3 3
Sample output
6 9 13
HINT
对于 100% 的数据, NM<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。
解析
树链剖分的模板题
#include<bits/stdc++.h>
#define int long long
using namespace std;
struct node{
int sum=0,l,r;
}tree[4000005];
int n,q,ans=0;
int a[1000005];
void build(int i,int l,int r){
tree[i].l=l;
tree[i].r=r;
if(l==r) return;
int mid=(l+r)/2;
build(i*2,l,mid);
build(i*2+1,mid+1,r);
}
int rea(int i){
if(tree[i].l==tree[i].r){
tree[i].sum=a[tree[i].r];
return tree[i].sum;
}
tree[i].sum=rea(i*2)+rea(i*2+1);
return tree[i].sum;
}
void jia(int i,int dis,int k){
tree[i].sum+=k;
if(tree[i].l==tree[i].r) return;
if(dis<=tree[i*2].r) jia(i*2,dis,k);
if(dis>=tree[i*2+1].l) jia(i*2+1,dis,k);
}
void query(int i,int l,int r){
if(tree[i].l>=l&&tree[i].r<=r){
ans+=tree[i].sum;
return;
}
if(tree[i*2].r>=l) query(i*2,l,r);
if(tree[i*2+1].l<=r) query(i*2+1,l,r);
}
main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>a[i];
build(1,1,n);
rea(1);
while(q--){
int op,z,x;
cin>>op>>z>>x;
if(op==1){
jia(1,z,x);
}
else{
ans=0;
query(1,z,x);
cout<<ans<<'\n';
}
}
}