zkw线段树初步

一、

单点修改+区间查询(就树状数组那个)

查询:

l为左儿子,则l的父亲节点被[l,r]包含,反之该区间内只有l被包含

同理 当r为右儿子,则r的父亲节点被[l,r]包含 

很容易理解 可以直接加上lr的兄弟节点来得到父亲节点的值,同时把lr上移 

lr是兄弟时,说明[l,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

 

你可能感兴趣的:(模板类(什么,这也是模板))