几种树状数组

1.单点修改,区间查询
题面:
给定数列a[1],a[2],…,a[n],你需要依次进行 q个操作,操作有两类:
1 i x:给定i,x,将a[i]加上x;
2 l r:给定l,r,求 ∑ri=la[i]的值(换言之,求a[l]+a[l+1]+⋯+a[r]的值)
Input
第一行包含2个正整数n,q,表示数列长度个数,保证a≤n,q≤106
第二行n个整数a[1],a[2],…,a[n],表示初始数列,保证|a[i]|≤106
接下来q行,每行一个操作,为下列两种之一:
1 i x:给定i,x,将a[i]加上x;
2 l r:给定l,r,求 ∑ri=1a[i]的值;
保证1≤l≤r≤n, |x|≤106
Output
对于每个 2 l r 操作输出一行,每行有一个整数,表示所求的结果。

代码:

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,n) for(ll i=j;i<=n;i++)
typedef long long ll;
ll n,q,aa[1111111],c[1111111],a,b,d;
ll lowbit(ll x){
	return x&(-x);
}
void change(ll i,ll k){
	while(i<=n){
		c[i]+=k;
		i+=lowbit(i);
	}
}
ll getsum(ll n){	
	ll ans=0;
	while(n>0){
		ans+=c[n];
		n-=lowbit(n);
	}
	return ans;
}


int main(){
	scanf("%lld%lld",&n,&q);
	
	rep(i,1,n){
	scanf("%lld",&aa[i]);
	change(i,aa[i]);}
	
	rep(i,1,q){
	scanf("%lld%lld%lld",&a,&b,&d);
	if(a==1){
	aa[b]+=d;
	change(b,d);}
	
	if(a==2)
	printf("%lld\n",getsum(d)-getsum(b-1));
	}
	return 0;
}

2.区间修改,单点查询
题面:
给定数列a[1],a[2],…,a[n] ,你需要依次进行 q 个操作,操作有两类:
1 l r x:给定 l,r,x,对于所有i∈[l,r],将 a[i] 加上 x(换言之,将 a[l],a[l+1],…,a[r] 分别加上 x);
2 i:给定 i ,求 a[i] 的值。
Input
第一行包含 2 个正整数 n,q,表示数列长度和询问个数。保证1≤n,q≤106 。
第二行 n 个整数 a[1],a[2],…,a[n],表示初始数列。保证|a[i]|≤106 。
接下来 q 行,每行一个操作,为以下两种之一:
1 l r x:对于所有 i∈[l,r],将a[i]加上x;
2 i:给定 i,求 a[i] 的值。
保证 1≤l≤r≤106, |x|≤106。

Output
对于每个 2 i 操作,输出一行,每行有一个整数,表示所求的结果。

代码:

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,n) for(ll i=j;i<=n;i++)
typedef long long ll;
ll n,q,aa[1111111],c[1111111],a,b,d,e;
ll lowbit(ll x){
	return x&(-x);
}
void change(ll i,ll k){
	while(i<=n){
		c[i]+=k;
		i+=lowbit(i);
	}
}
ll getsum(ll n){	
	ll ans=0;
	while(n>0){
		ans+=c[n];
		n-=lowbit(n);
	}
	return ans;
}
void update(ll x, ll v) {
    for (ll i = x; i <= n; i += lowbit(i))
	 c[i] += v;
}
ll query(ll x) {
    ll sum = 0;
    for (ll i = x; i >= 1; i -= lowbit(i)) sum += c[i];
    return sum;
}


int main(){
	scanf("%lld%lld",&n,&q);
	
	rep(i,1,n){
	scanf("%lld",&aa[i]);
	change(i,aa[i]-aa[i-1]);}
	
	rep(i,1,q){
	scanf("%lld",&a);
	if(a==1){
		scanf("%lld%lld%lld",&b,&d,&e);{
		update(b,e);
		update(d+1,-e);
		}
	}
	if(a==2){
		scanf("%lld",&b);{
		printf("%lld\n",query(b));
		}
	}
	}
	return 0;
}

3.区间修改,区间查询
给定数列 a[1],a[2],…,a[n],你需要依次进行q个操作,操作有两类:
1 l r x:给定l,r,x,对于所有的i∈[l,r],将a[i]加上x(换言之,将a[l],a[l+1],…,a[r] 分别加上x)
2 l r:给定l,r,求∑ri=la[i]的值(换言之,求a[l]+a[l+1]+…+a[r]的值)
Input
第一行包含2个正整数n,q,表示数列长度和询问个数。保证1≤n,q≤106;
第二行n个整数a[1],a[2],…,a[n],表示初始数列。保证|a[i]|≤106。
接下来q行,每行一个操作,为以下两种之一:
1 l r x:对于所有的i∈[l,r],将a[i]加上x;
2 l r:输出∑ri=la[i]的值;
Output
对于每个 2 l r 操作,输出一行,每行有一个整数,表示所求的结果。

代码:

/*aa原数组
sum1[]:维护aa[]的差分数组的 树状数组;
sum2[]:sum2[i]=(i-1)*sum1[i];
令ans[i]=n*sum1[i]-sum2[i]:ans[]为维护aa[]的前缀和数组的 树状数组;
具体推导过程 另查阅~
*/
#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,n) for(ll i=j;i<=n;i++)
typedef long long ll;
ll sum1[2222222],sum2[2222222],aa[2222222];
ll a,l,r,x,n,q;

ll lowbit(ll n){
	return n&(-n);
}

ll change(ll i, ll k){
	ll x=i;
	while(i<=n){
		sum1[i]+=k;
		sum2[i]+=k*(x-1);
		i+=lowbit(i);
	}
}
ll getsum(ll n){
	ll ans=0,x=n;
	while(n>0){
		ans+=x*sum1[n]-sum2[n];
		n-=lowbit(n);
	}
	return ans;
}
int main(){
	scanf("%lld%lld",&n,&q);
	
	rep(i,1,n){
		scanf("%lld",&aa[i]);
		change(i,aa[i]-aa[i-1]);
	}
	
	while(q--){
		scanf("%lld",&a);
		if(a==1){
		scanf("%lld%lld%lld",&l,&r,&x);	
			change(l,x);
			change(r+1,-x);
		}
		if(a==2){
		scanf("%lld%lld",&l,&r);
		printf("%lld\n",getsum(r)-getsum(l-1));		
		}
	}

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值