数据结构颓废计划II-树状数组的推广与应用

3 篇文章 0 订阅
1 篇文章 0 订阅

二维树状数组

基础知识

若将树状数组与前缀和对应起来,那么二维树状数组就可以与二维前缀和对应起来。此时循环不适合以while的形式出现,而适合以for的嵌套的形式出现。单点修改与区间查询的顺序还是一样。

模板

int lowbit ( int x )
 {
    return x&(-x);
 }
void update( int x , int y , int delta )
 {
    for ( int i=x;i<=N;i+=lowbit(i))
       for ( int j=y;j<=N;j+=lowbit(j))
         Fenwick_Tree[i][j]+=delta;
 }

int query( int x , int y )
 {
    int Ans=0;
    for ( int i=x;i>0;i-=lowbit(i))
       for ( int j=y;j>0;j-=lowbit(j))
         Ans+=Fenwick_Tree[i][j];
    return Ans;
 }

应用

POJ1195 IOI2001 Mobile Phones

裸题。

代码
#include <stdio.h>
#include <string.h>
int Fenwick_tree[1050][1050],N=5;
int lowbit ( int x )
 {
    return x&(-x);
 }
void update( int x , int y , int delta)
 {
    for ( int i=x;i<=N;i+=lowbit(i))
       for ( int j=y;j<=N;j+=lowbit(j))
         Fenwick_tree[i][j]+=delta;
 }
int query( int x , int y )
 {
    int Ans=0;
    for ( int i=x;i>0;i-=lowbit(i))
       for ( int j=y;j>0;j-=lowbit(j))
         Ans+=Fenwick_tree[i][j];
    return Ans;
 }
int main()
 {
    int Oper,x,y,a,x1,x2,y1,y2;
    memset(Fenwick_tree,0,sizeof(Fenwick_tree));
    while(scanf("%d",&Oper)!=EOF)
      {
          if(Oper==0)
           {
               scanf("%d",&N);
               N++;
              }
          else if(Oper==1)
           {
              scanf("%d%d%d",&x,&y,&a);
              x++,y++;
              update(x,y,a);
           }
          else if(Oper==2)
           {
               scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
               x2++,y2++;
               printf("%d\n",query(x2,y2)+query(x1,y1)-query(x1,y2)-query(x2,y1));

           }
           else if(Oper==3)
             break;
       }
    return 0;
 }
POJ2155 Matrix 楼天城男人八题
做法

TODO

代码

TODO

区间修改

做法

维护一个增量数组 delta deltax 代表 [1,X] 上的区间增量。在对 [L,R] 进行区间修改时,我们将 [1,L] 区间减增量,即 deltaL 减去相应增量,而 [1,R] 区间增增量,即 deltaR 增加相应增量。最终求区间和时,有:

i=1XAi=i=1XOrigini+(i=1Xdeltai)(i+1)i=1X(deltaii)

用递推可以维护 Ai ,那么用树状数组维护 deltai deltaii 的前缀和即可。

例题

CodeVS1082 线段树练习3
题解

不解释

代码
#include <stdio.h>
long long Fenwick_Tree1[200001],Fenwick_Tree2[200001],A[200001],N;
long long lowbit(long long x)
 {
    return x&(-x);
 }
void update( long long i , long long x , long long* target )
 {
     while(i<=N)
      {
         target[i]+=x;
         i+=lowbit(i);
       }
    return ;
 }
long long query ( long long i , long long *target)
 {
    long long Ans=0;
    while(i>0)
     {
        Ans+=target[i];
        i-=lowbit(i);
      }
    return Ans;
 }
long long in()
 {
     long long Ans=0;
     char ch=getchar();
     char bi=1;
     while(ch>'9'||ch<'0') {
       ch=getchar();
       if(ch=='-') bi=-1;
}
     while(ch>='0'&&ch<='9'){
          Ans=Ans*10+ch-'0';
          ch=getchar();
      }
     return bi*Ans;
 }
int main()
 {
     N=in();
     for ( int i=1;i<=N;i++)
        {
           A[i]=in();
           A[i]+=A[i-1];
        }
     long long Q=in(),op,L,R,x;
     for ( int i=1;i<=Q;i++)
       {
           op=in();
           if(op==1)
            {
                L=in(),R=in(),x=in();
                update(L,x,Fenwick_Tree1);
                update(R+1,-x,Fenwick_Tree1);
                update(L,x*L,Fenwick_Tree2);
                update(R+1,-x*(R+1),Fenwick_Tree2);
            }
            else  
             {
                 L=in(),R=in();
                 L=A[L-1]+L*query(L-1,Fenwick_Tree1)-query(L-1,Fenwick_Tree2);
                 R=A[R]+(R+1)*query(R,Fenwick_Tree1)-query(R,Fenwick_Tree2);
                 printf("%lld\n",R-L);
             }
        }
 }

杂项

树状数组求逆序对

做法其实和I中的Star一题相似,先离散化,然后过程几乎相同,但是需要修改计数的对象。这里不写了。

树状数组求RMQ

TODO

树状数组的作用,局限和推广

作用

以常数较低,空间浪费较少的方式,处理区间问题。

局限

必须在区间某运算与其逆运算均成立的情况下使用。

推广:ST表

ST表思想与树状数组相同。但它比树状数组存储的信息多一点,如果我们将树状数组前后倒置,即得到ST表的一部分。我们即不需要满足区间运算性质,而直接运用合并的方法来处理这个问题。容易证明,其空间复杂度 O(Nlog2N) ,单次查询的时间复杂度 O(log2N) ,但如果进行修改,其复杂度即可达到平方阶。用ST表可解决无修改的RMQ,区间求和等问题。常数相较线段树更小。

例题:SHTSC 200X CLIMB
解法

一道需要注意细节的水题,用线段树和树状数组可能会被卡常。

代码
#include <math.h>
#include <stdio.h>
#include <string.h>
using namespace std;
short RMQ[5000001][30];
int N,Q,l,r;
short max(short a,short b)
 {
      return a>b?a:b;
}
short query( int l , int r )
 {
           short x=(short)(log(r-l+1)/log(2));
           return max(RMQ[l][x],RMQ[r+1-(1<<x)][x]);
}
int main()
 {
         freopen("climb.in","r",stdin);
         freopen("climb.out","w",stdout);
          memset(RMQ,0,sizeof(RMQ));
          scanf("%d",&N);
          for ( int i=1;i<=N+1;i++)
             scanf("%d",&RMQ[i][0]);
          for ( int j=1;j<=log(N+1)/log(2);j++) 
              for ( int i=1;i+(1<<j)-1<=N+1;i++)
                 RMQ[i][j]=max(RMQ[i][j-1],RMQ[i+(1<<(j-1))][j-1]);
          scanf("%d",&Q);
          for ( int i=1;i<=Q;i++)
            {
                scanf("%d%d",&l,&r);
                printf("%d\n",query(l+1,r+1));
            }
          return 0;
 }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值