一维树状数组(单点修改+区间查找 / 区间修改+单点查找 / 区间修改+区间查找)模板和例题

本文深入介绍了树状数组(也称作CSECT)的数据结构,包括其初始化、单点修改和区间查询操作,并提供了三个不同应用场景的代码实现:单点修改+区间查询、区间修改+单点查询以及区间修改+区间查值。通过这些实例,读者可以理解如何利用树状数组高效地处理动态更新和查询问题。
摘要由CSDN通过智能技术生成

模板1题目地址
模板2题目地址
模板3题目地址

可以先看看这位博主的介绍

可以用update(O(logn))初始化,也可以用init(O(n))初始化。
单点修改 + 区间查询
1:

#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
//#define int long long
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const double eps = 1e-5;
const int mod = 2013;
const int N = 5e5+10;

int c[N];
int n,m,a;
int lowbit(int x){ return x & -x;}

void update(int x,int y){
	for(int i=x;i<=n;i+=lowbit(i)) c[i] += y;
}

void init(int x,int b){
	int y = x; x = lowbit(x) >> 1;
	c[y] += b;
	while(x){
		c[y] += c[y-x];
		x >>= 1;
	}
}

int ask(int x){
	int sum = 0;
	for(int i=x;i;i-=lowbit(i)) sum += c[i];
	return sum;
}

signed main(){
	IOS
    #ifdef ddgo
		freopen("C:\\Users\\asus\\Desktop\\ddgoin.txt","r",stdin);
    #endif
	
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a,init(i,a);//update(i,a);
	while(m --){
		int k,x,y; cin>>k>>x>>y;
		if(k == 1) update(x,y);
		else cout<<ask(y)-ask(x-1)<<endl;
	}
	
    return 0;
    
}

例题
这里有一道树状数组与逆序对得例题。
题目地址

因为我们维护的是前缀和,从后往前判断比较方便。
对于一个数,只要我们找到它后面有x个比它小的,那么答案 += x.
处理过程
1: 询问当前的 1~a[i-1]的和,表示在当前这个数之前,已经出现了比这个数小的个数。
2: 当前询问完后,对其a[x]位置进行一个 +1 的维护,表示这个数出现次数+1.
3: 记住离散化

#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const double eps = 1e-5;
const int mod = 2013;
const int N = 1e6+10;

int c[N],a[N],b[N];
int n,m;
int lowbit(int x){ return x & -x;}

void update(int x,int y){
	for(int i=x;i<=n;i+=lowbit(i)) c[i] += y;
}

int ask(int x){
	int sum = 0;
	for(int i=x;i;i-=lowbit(i)) sum += c[i];
	return sum;
}

int find(int x){
	return lower_bound(b+1,b+m,x)-b;
}

signed main(){
	IOS
    #ifdef ddgo
		freopen("C:\\Users\\asus\\Desktop\\ddgoin.txt","r",stdin);
    #endif
	
	cin>>n;
	int res = 0;
	for(int i=1;i<=n;i++) cin>>a[i],b[i] = a[i];
	sort(b+1,b+n+1);
	m = unique(b+1,b+n+1)-b;
	for(int i=n;i;i--){
		int t = find(a[i]);
		res += ask(t-1);
		update(t,1);
	}
	cout<<res<<endl;
    return 0;
}

区间修改 + 单点查询

2:

#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
//#define int long long
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const double eps = 1e-5;
const int mod = 2013;
const int N = 5e5+10;

int c[N];
int n,m,a;
int lowbit(int x){ return x & -x;}

void update(int x,int y){
	for(int i=x;i<=n;i+=lowbit(i)) c[i] += y;
}

int ask(int x){
	int sum = 0;
	for(int i=x;i;i-=lowbit(i)) sum += c[i];
	return sum;
}

signed main(){
	IOS
    #ifdef ddgo
		freopen("C:\\Users\\asus\\Desktop\\ddgoin.txt","r",stdin);
    #endif
	
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a,update(i,a),update(i+1,-a); 
	while(m --){
		int k,x,y,z; cin>>k;
		if(k == 1){
			cin>>x>>y>>z;
			update(x,z);
			update(y+1,-z);
		}
		else{
			cin>>x;
			cout<<ask(x)<<endl;
		}
	}
	
    return 0;
    
}

区间修改 + 区间查值
用差分思想 求区间的值我们得到 ∑ i = 1 p ∑ j = 1 i c [ i ] \sum_{i=1}^{p}\sum_{j=1}^{i}c[i] i=1pj=1ic[i]
可以知道 ∑ i = 1 p c [ i ] ∗ ( p − i + 1 ) \sum_{i=1}^{p}c[i]*(p-i+1) i=1pc[i](pi+1)
再化简得 ( p + 1 ) ∗ ∑ i = 1 p c [ i ] (p+1)*\sum_{i=1}^{p}c[i] (p+1)i=1pc[i] - ∑ i = 1 p c [ i ] ∗ i \sum_{i=1}^{p}c[i]*i i=1pc[i]i
所以我们多维护一个c2[i] 存放 c[i]*i。

3:

#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const double eps = 1e-5;
const int mod = 2013;
const int N = 1e6+10;

int c1[N],c2[N];
int n,m,a;
int lowbit(int x){ return x & -x;}

void update(int x,int y){
	for(int i=x;i<=n;i+=lowbit(i)) c1[i] += y,c2[i] += x*y;
}

int ask(int x){
	int sum = 0;
	for(int i=x;i;i-=lowbit(i)) sum += (x + 1)*c1[i] - c2[i];
	return sum;
}

signed main(){
	IOS
    #ifdef ddgo
		freopen("C:\\Users\\asus\\Desktop\\ddgoin.txt","r",stdin);
    #endif
	
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a,update(i,a),update(i+1,-a); 
	while(m --){
		int k,x,y,z; cin>>k;
		if(k == 1){
			cin>>x>>y>>z;
			update(x,z);
			update(y+1,-z);
		}
		else{
			cin>>x>>y;
			cout<<ask(y)-ask(x-1)<<endl;
		}
	}
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值