树状数组

树状数组

一、区间求和&&单点修改

树状数组是一个查询和修改复杂度都为log(n)的数据结构。主要用于数组的单点修改&&区间求和

C [ i ] = A [ i - 2^k + 1 ] + A [ i - 2^k + 2 ] + ...... A [ i ] ; (k为i的二进制中从最低位到高位连续零的长度)

树状数组一般是单点修改和区间查询,如果是区间修改和单点查询的话,可以用差分。 d [ i ] = a [ i ] - a[ i - 1 ] , a [ i ] = sum( d [ j ] )

# include <bits/stdc++.h>
using namespace std;

const int MAXN=5e5+100;
int c[MAXN],a[MAXN]; //a原数组 c求和后数组
int lowbit(int x){ return x&(-x); }
void add(int x,int y,int n)  //x更新位置 y为更新后的数  
{
for(int i=x;i<=n;i+=lowbit(i)){
c[i]+=y;
}
return ;
}
int sum(int x) //1-x 求和
{
int ans=0;
for(int i=x;i;i-=lowbit(i)){
ans+=c[i];
}
return ans;
}
int main()
{
int n,m; scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
add(i,a[i],n);
}
while(m--){
int op; scanf("%d",&op);
if(op==1){
int x,k; scanf("%d%d",&x,&k);
add(x,k,n);
}else if(op==2){
int x,y; scanf("%d%d",&x,&y);
int cnt=sum(y)-sum(x-1);
printf("%d\n",cnt);
}
}

return 0;
}

区间修改&&区间查询

sumi(1,p) a [ i ] = sumi(1,p)sumj(1,i) d [ j ]

= sumi(1,p) d [ i ] * ( p - i +1) = ( p+1 ) * sumi(1,p) d [ i ] - sumi( 1, p) d [ i ] * i;

# include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int MAXN=1e5+100;
int a[MAXN],d[MAXN],sum1[MAXN],sum2[MAXN];
int lowbit(int x) { return x&(-x); }
void add(LL x,LL y,int n)
{
for(int i=x;i<=n;i+=lowbit(i)){
sum1[i]+=y,sum2[i]+=y*x;
}
return ;
}
void range_add(LL l,LL r,LL y,int n)
{
add(l,y,n),add(r+1,-y,n);
return ;
}
LL sum(LL x)
{
LL ans=0;
for(int i=x;i;i-=lowbit(i)){
ans+=(x+1)*sum1[i]-sum2[i];
}
return ans;
}
LL range_sum(LL l,LL r)
{
return sum(r)-sum(l-1);
}
int main()
{
int n; scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
d[i]=a[i]-a[i-1];
add(i,d[i],n);
}
range_add(2,4,1,n);
for(int i=1;i<=n;i++){
cout<<i<<" "<<sum(i)<<endl;
}
cout<<range_sum(1,n)<<endl;
return 0;
}

二、二维树状数组

由于一般提供的是加减操作,遇到其他操作思考能否转化为加减操作。

单点修改&&区间查询

# include <bits/stdc++.h>
using namespace std;

const int MAXN=1e3+100;
int c[MAXN][MAXN];
int lowbit(int x){ return x&(-x); }
void add(int x,int y,int z,int n) //将点(x,y)加上z
{
for(int i=x;i<=n;i+=lowbit(i)){
for(int j=y;j<=n;j+=lowbit(j)){
c[i][j]+=z;
}
}
return ;
}
int sum(int x,int y) //求左上角为(1,1)右下角为(x,y)的矩阵和
{
int ans=0;
for(int i=x;i;i-=lowbit(i)){
for(int j=y;j;j-=lowbit(j)){
ans+=c[i][j];
}
}
return ans;
}
int main()
{
int n; scanf("%d",&n);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int a;
scanf("%d",&a);
add(i,j,a,n);
}
}
int x,y; scanf("%d%d",&x,&y);
printf("%d\n",sum(x,y));
return 0;
}
/*
5
1 2 0 1 3
3 4 1 2 0
1 3 2 1 4
0 0 1 2 1
0 3 2 1 0
3 4
*/

区间修改&&单点查询

d [ i ] [ j ] = a [ i ] [ j ] - ( a [ i - 1 ] [ j ] + a [ i ] [ j - 1 ] - a [ i - 1 ] [ j - 1 ] )

# include <bits/stdc++.h>
using namespace std;

const int MAXN=1e4+100;
int a[MAXN][MAXN],c[MAXN][MAXN],d[MAXN][MAXN];
int lowbit(int x) { return x&(-x); }

void add(int x,int y,int z,int n)
{
for(int i=x;i<=n;i+=lowbit(i)){
for(int j=y;j<=n;j+=lowbit(j)){
c[i][j]+=z;
}
}
return ;
}
void range_add(int ax,int ay,int bx,int by,int z,int n)
{
add(ax,ay,z,n);
add(bx+1,by+1,z,n);
add(bx+1,ay,-z,n);
add(ax,by+1,-z,n);
return ;
}
int sum(int x,int y)  //差分的和即为原数组
{
int ans=0;
for(int i=x;i;i-=lowbit(i)){
for(int j=y;j;j-=lowbit(j)){
ans+=c[i][j];
}
}
return ans;
}
int main()
{
int n; scanf("%d",&n);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&a[i][j]);
d[i][j]=a[i][j]-(a[i-1][j]+a[i][j-1]-a[i-1][j-1]);
add(i,j,d[i][j],n);
}
}
int ax,ay,bx,by,z; scanf("%d%d%d%d%d",&ax,&ay,&bx,&by,&z);
range_add(ax,ay,bx,by,z,n);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<sum(i,j)<<" ";
}
cout<<endl;
}
return 0;
}
/*
5
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
*/

区间修改&&区间查询

二维前缀和:sumi(1,x)sumj(1,y)sumk(1,i)sumh(1,j) d [ h ] [ k ]

=( x + 1 ) * ( y + 1 ) * sumi(1,x)sumj(1,y)d [ i ] [ j ] - ( y + 1 ) * sumi(1,x)sumj(1,y)d [ i ] [ j ] * i - (x+1) * sumi(1,x)sumj(1,y) d[ i ] [ j ] * j + sumi(1,x)sumj(1,y) d[ i ] [ j ] * i * j

# include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int MAXN=1e3+100;
LL a[MAXN][MAXN],d[MAXN][MAXN];
LL sum1[MAXN][MAXN],sum2[MAXN][MAXN],sum3[MAXN][MAXN],sum4[MAXN][MAXN];
int lowbit(int x) { return x&(-x); }
void add(LL x,LL y,LL z,LL n,LL m)
{
for(int i=x;i<=n;i+=lowbit(i)){
for(int j=y;j<=m;j+=lowbit(j)){
sum1[i][j]+=z;
sum2[i][j]+=z*x;
sum3[i][j]+=z*y;
sum4[i][j]+=z*x*y;
}
}
return ;
}
void range_add(LL ax,LL ay,LL bx,LL by,LL z,LL n,LL m)// (xa,ya) 到 (xb,yb) 的矩形
{
add(ax,ay,z,n,m);
add(bx+1,by+1,z,n,m);
add(bx+1,ay,-z,n,m);
add(ax,by+1,-z,n,m);
return ;
}
LL sum(LL x,LL y)
{
LL ans=0;
for(int i=x;i;i-=lowbit(i)){
for(int j=y;j;j-=lowbit(j)){
ans+=(x+1)*(y+1)*sum1[i][j]-(y+1)*sum2[i][j]-(x+1)*sum3[i][j]+sum4[i][j];
}
}
return ans;
}
LL range_sum(LL ax,LL ay,LL bx,LL by)
{
return sum(bx,by)-sum(bx,ay-1)-sum(ax-1,by)+sum(ax-1,ay-1);
}
int main()
{
int T; scanf("%d",&T);
while(T--){
memset(sum1,0,sizeof(sum1));
memset(sum2,0,sizeof(sum2));
memset(sum3,0,sizeof(sum3));
memset(sum4,0,sizeof(sum4));
int n,m; scanf("%d%d",&n,&m);
while(m--){
char c; cin>>c;
if(c=='C'){
LL ax,ay,bx,by; cin>>ax>>ay>>bx>>by;
range_add(ax,ay,bx,by,1,n,n);
}else{
LL x,y; cin>>x>>y;
cout<<range_sum(x,y,x,y)%2<<endl;
}
}

cout<<endl;
}

return 0;
}

三、区间最大值

# include <bits/stdc++.h>
using namespace std;

const int MAXN=1e5+100;
int a[MAXN],t[MAXN];
int lowbit(int x) { return x&(-x); }
void add(int x,int y,int n)
{
for(int i=x;i<=n;i+=lowbit(i)){
t[i]=max(t[i],y);
}
return ;
}
int query(int x)
{
int ans=0;
for(int i=x;i>0;i-=lowbit(i)){
ans=max(ans,t[i]);
}
return ans;
}
int main()
{
int n; scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
add(i,a[i],n);
}
for(int i=1;i<=n;i++){
printf("%d %d\n",i,query(i));
}

return 0;
}
/*
5
1 3 2 4 5
*/

四、逆序对

应为会用到输入数组作为下标,对内存要求很大,一般不使用

# include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int MAXN=1e5+100;
int a[MAXN],b[MAXN];
int lowbit(int x){ return x&(-x); }
void add(int x,int n)
{
for(int i=x;i<=n;i+=lowbit(i)){
a[i]++;
}
return ;
}
int sum(int x)
{
int ans=0;
for(int i=x;i>0;i-=lowbit(i)){
ans+=a[i];
}
return ans;
}
int main()
{
int n; scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&b[i]);
}
LL res=0;
for(int i=1;i<=n;i++){
add(b[i],n);
res+=(LL)(i-sum(b[i]));
}
printf("%lld\n",res);
return 0;
}



posted @   fengzlj  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示