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

来源:互联网 发布:mac版adobe flash cs6 编辑:程序博客网 时间:2024/05/29 07:36

二维树状数组

基础知识

若将树状数组与前缀和对应起来,那么二维树状数组就可以与二维前缀和对应起来。此时循环不适合以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

区间修改

做法

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

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

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

例题

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 0
原创粉丝点击