一、
单点修改+区间查询(就树状数组那个)
查询:
当为左儿子,则
的父亲节点被
包含,反之该区间内只有
被包含
同理 当为右儿子,则r的父亲节点被
包含
很容易理解 可以直接加上和
的兄弟节点来得到父亲节点的值,同时把
和
上移
当和
是兄弟时,说明
的区间中所有数已经被全部包含
int M,n,tree[maxn<<2];
void push_up(int x){
tree[x] = tree[x<<1]+tree[x<<1|1];
}
void build(int n){
for(M=1;M>=1;pos;pos>>=1)push_up(pos);
}
int query(int l,int r,int ans = 0){
for(l = M+l-1,r = M+r+1;l^r^1;l>>=1,r>>=1){/// [l,r] --> (l-1,r+1)
if(l&1^1) ans += tree[l^1];/// l%2 == 0, tree[l+1]
if(r&1) ans += tree[r^1];/// r%2 == 1, tree[r-1]
}
return ans;
}
区间更新+区间最值
#include
using namespace std;
typedef long long ll;
const int maxn = 2e5+7;
int A[maxn],n,q,N;
ll tree[maxn<<2];
void push_up(int x,ll Mx = 0){
Mx = max(tree[x],tree[x^1]);
tree[x] -= Mx,tree[x^1] -= Mx;
tree[x>>1] += Mx;
}
void build(int n){
for(N=1;N>1];
}
void update(int s,int t,ll C){
for(s=N+s-1,t=N+t+1;s^t^1;s>>=1,t>>=1){
if(~s&1) tree[s^1]+=C;
if( t&1) tree[t^1]+=C;
push_up(s),push_up(t);
}
while(s>1)push_up(s),s>>=1;
}
ll query(int s,int t,ll Lans = 0,ll Rans = 0){
s += N,t += N;
if(s != t){
for(;s^t^1;s>>=1,t>>=1){
Lans += tree[s];
Rans += tree[t];
if(~s&1) Lans = max(Lans,tree[s^1]);
if( t&1) Rans = max(Rans,tree[t^1]);
}
}
ll ans = max(Lans+tree[s],Rans+tree[t]);
while(s>1) ans += tree[s>>=1];
return ans;
}
int main(){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)scanf("%d",&A[i]);
build(n);
while(q--){
int l,r,flag;
scanf("%d%d%d",&flag,&l,&r);
if(flag == 2)printf("%lld\n",query(l,r));
else scanf("%d",&flag),update(l,r,flag);
}
return 0;
}
/*
5 1000
1 2 3 4 5
2 3 3
2 1 2
1 2 4 3
2 3 3
2 1 1
2 2 2
*/
单点更新+区间更新+区间求和
代码,洛谷模板线段树p3372
#include
using namespace std;
typedef long long ll;
const int maxn = 2e5+7;
int A[maxn],n,q,N;//原数组,n为原数组元素个数 ,N为扩充元素个数
ll Sum[maxn<<2];//区间和
ll Add[maxn<<2];//懒惰标记
void build(int n){
//计算N的值
N=1;while(N < n+2) N <<= 1;
//更新叶节点
for(int i=1;i<=n;++i) Sum[N+i]=A[i];//原数组下标+N=存储下标
//更新非叶节点
for(int i=N-1;i>0;--i){
//更新所有非叶节点的统计信息
Sum[i]=Sum[i<<1]+Sum[i<<1|1];
//清空所有非叶节点的Add标记
Add[i]=0;
}
}
void update(int L,int C)
{
for(int s=N+L;s;s>>=1)
Sum[s]+=C;
}
void update(int L,int R,int C){
ll s,t,Ln=0,Rn=0,x=1;
//Ln: s一路走来已经包含了几个数
//Rn: t一路走来已经包含了几个数
//x: 本层每个节点包含几个数
for(s=N+L-1,t=N+R+1;s^t^1;s>>=1,t>>=1,x<<=1){
//更新Sum
Sum[s]+=C*Ln;
Sum[t]+=C*Rn;
//处理Add
if(~s&1) Add[s^1]+=C,Sum[s^1]+=C*x,Ln+=x;
if( t&1) Add[t^1]+=C,Sum[t^1]+=C*x,Rn+=x;
}
//更新上层Sum
for(;s;s>>=1,t>>=1){
Sum[s]+=C*Ln;
Sum[t]+=C*Rn;
}
}
ll query(int L,int R){
ll s,t,Ln=0,Rn=0,x=1;
ll ANS=0;
for(s=N+L-1,t=N+R+1;s^t^1;s>>=1,t>>=1,x<<=1){
//根据标记更新
if(Add[s]) ANS+=Add[s]*Ln;
if(Add[t]) ANS+=Add[t]*Rn;
//常规求和
if(~s&1) ANS+=Sum[s^1],Ln+=x;
if( t&1) ANS+=Sum[t^1],Rn+=x;
}
//处理上层标记
for(;s;s>>=1,t>>=1){
ANS+=Add[s]*Ln;
ANS+=Add[t]*Rn;
}
return ANS;
}
int main(){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)scanf("%d",&A[i]);
build(n);
while(q--){
int l,r,flag;
scanf("%d%d%d",&flag,&l,&r);
if(flag == 2)printf("%lld\n",query(l,r));
else scanf("%d",&flag),update(l,r,flag);
}
return 0;
}
想输出看一下建成的线段树长啥样?ok这个代码,但是只支持树上点的值在0到9范围内
void out(int k = 1){
int a = N-1,b = 4*N-1;
for(int i=1;i