• ST表与树状数组


    ST表 

     st表可以解决区间最值的问题。可以做到O(nlogn)预处理 ,O(1)查询,但是不支持修改。

      st表的大概思路就是用st[i][j]来表示从i开始的2的j次方个树中的最值,查询时就从左端点开始,找到区间长度是2的多少次方,然后进行查询。然而,很明显,我们要查询的区间长度不一定是2的多少次幂。那怎么做到O(1)查询呢,这就要用到最值的特性。

    如图,假如我们要查询2到7之间的最大值,但是7-2+1在22与23之间,我们选择22,也就是st[2][2],那剩下的6,7怎么办,我们考虑倒着从7往回算,也就是在st[7-22][2]与st[2][2]取max作为从2到7的最大值。

      首先,进行预处理,st[i][j]表示从i开始的2的j次方,那么st[i][j]就应该是从i开始2的j-1次方与从i+2j-1开始的2的j-1次方中的最大值,那么进行递推就好了。

    代码:

     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 const int N=100100;
     5 int n,m,a[N],st[N][20],log[N],cf[20];
     6 void pre()
     7 {
     8   log[2]=1;
     9   log[1]=0;
    10   for(int i=3;i<=n;++i)
    11   {
    12     log[i]=log[i/2]+1;
    13   }
    14   cf[0]=1;
    15   cf[1]=2;
    16   for(int i=2;i<=log[n]+1;++i)
    17   {
    18     cf[i]=cf[i-1]*2;
    19   }
    20 }
    21 int read()
    22 {
    23   int x=0,f=1;char c=getchar();
    24   while(c<'0'||c>'9')
    25   {
    26     if(c=='-') f=-1;
    27     c=getchar();
    28   }
    29   while(c>='0'&&c<='9')
    30   {
    31     x=x*10+c-'0';
    32     c=getchar();
    33   }
    34   return x;
    35 }
    36 int ff(int x,int y)
    37 {
    38   int l=y-x+1,k=log[l];
    39   int f=max(st[x][k],st[y-cf[k]+1][k]);
    40   return f;
    41 }
    42 int main()
    43 {
    44   n=read(),m=read();
    45   pre();
    46   for(int i=1;i<=n;++i)
    47   {
    48     st[i][0]=a[i]=read();
    49 
    50   }
    51 
    52   for(int j=1;j<=log[n];++j)
    53   {
    54     for(int i=1;i+cf[j]-1<=n;++i)
    55     {
    56       st[i][j]=max(st[i][j-1],st[i+cf[j-1]][j-1]);
    57     }
    58   }
    59   for(int i=1,x,y;i<=m;++i)
    60   {
    61     x=read(),y=read();
    62     cout<<ff(x,y)<<"
    ";
    63   }
    64   return 0;
    65 }

     树状数组

    其实,树状数组的原理我并不是很懂,但是因为其短小精炼的代码,令我非常喜欢。。。。

    树状数组不需要预处理,只有修改与查询两种操作。修改可以是加或减一个值,查询的是一个区间和。

    首先我们需要一个数组tree。

    然后就是修改,只需写一个几行的子函数,然后将修改元素的下标和要加的元素传入函数,然后奇迹就发生了。

    查询传入要查询的下标就可以查询从1到改元素之间的区间和,如果查询从l到r,只需分别求出其从以到他们的区间和,然后相减即可,类似于前缀和。具体的都在代码里了。

     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 const int N=500005;
     5 int tree[N],n,m;
     6 void add(int a,int pos)
     7 {
     8     while(pos<=n){
     9         tree[pos]+=a;
    10         pos+=pos&-pos;
    11     }
    12 }
    13 int cc(int pos)
    14 {
    15     int ans=0;
    16     while(pos>=1){
    17         ans+=tree[pos];
    18         pos-=pos&-pos;
    19     }
    20     return ans;
    21 }
    22 int main()
    23 {
    24     scanf("%d%d",&n,&m);
    25     for(int i=1,x;i<=n;++i)
    26     {
    27         scanf("%d",&x);
    28         add(x,i);
    29     }
    30     for(int i=1,bz,x,y;i<=m;++i){
    31         scanf("%d%d%d",&bz,&x,&y);
    32         if(bz==1)
    33             add(y,x);
    34         else{
    35             int kk=cc(y),zz=cc(x-1);
    36             printf("%d
    ",kk-zz);
    37         }
    38     }
    39     return 0;
    40 }

     上面讲的是树状数组的单点修改和区间查询,下面写一下,树状数组的区间修改单点查询。

    首先介绍一下差分数组和前缀和。

    前缀和就是记录前几个数的和,差分数组呢就是记录当前位置减去前一个位置的数的差。

    然后要用到一个前缀和与差分数组的性质:前缀和数组的差分数组是原数组,差分数组的前缀和是原数组。

    证明很显然,动手一推就知道了。

    那么这与树状数组有什么关系呢,通过上面那个树状数组,我们知道,树状数组可以记录前几个数的和,现在我们要做的是区间修改和单点查询。

    这时又要用到一个差分数组的一个性质。

    差分数组进行区间加减时比较方便,比如如果将从i到j之间的数加k,那么只需将他们的差分数组i位置+k,并且将j+1位置的数-k即可。

    证明同样很显然,动手推。

    那么这就有联系了,我们树状数组维护的相当于是前缀和,然后我们如果用他维护原数组的差分数组,那么他就相当于维护的原数组,这样利用第一个性质单点查询就解决了。再就是区间修改,因为我们维护的是差分数组,所以利用性质二进行区间修改就好了。

    看代码就很清楚明了了

     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 const int N=500010;
     5 int n,m,a[N],tree[N];
     6 void read(int &x)
     7 {
     8   x=0;int f=1;char c=getchar();
     9   while(c<'0'||c>'9') {  if(c=='-') f=-1; c=getchar(); }
    10   while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar();}
    11   x=x*f;
    12 }
    13 void add(int pos,int w)
    14 {
    15   while(pos<=n)
    16   {
    17     tree[pos]+=w;
    18     pos+=pos&-pos;
    19   }
    20   return;
    21 }
    22 int ff(int pos)
    23 {
    24   int ans=0;
    25   while(pos>=1)
    26   {
    27     ans+=tree[pos];
    28     pos-=pos&-pos;
    29   }
    30   return ans;
    31 }
    32 
    33 
    34 int main()
    35 {
    36   int last=0;
    37   read(n),read(m);
    38   for(int i=1,x;i<=n;++i)
    39   {
    40     read(x);
    41     add(i,x-last);
    42     last=x;
    43   }
    44   for(int i=1,x,y,z,k;i<=m;++i)
    45   {
    46     read(x);
    47     if(x==1)
    48     {
    49       read(y),read(z),read(k);
    50       add(y,k);
    51       add(z+1,-k);
    52     }
    53     else if(x==2)
    54     {
    55       read(y);
    56       printf("%d
    ",ff(y));
    57     }
    58   }
    59   return 0;
    60 }
  • 相关阅读:
    R语言学习——数据分析
    java学习——类之YuanZhu
    java学习——类之YuanZhu
    java学习——构造类之3!+5!=126
    C# WebQQ协议群发机器人(二)
    Unity5.1 新的网络引擎UNET(七) UNET 单人游戏转换为多人
    转:Oracle GoldenGate学习之Goldengate介绍
    cvReadTrainData
    使用Handler在子线程中更新UI
    初识MVC
  • 原文地址:https://www.cnblogs.com/wxyww/p/8992279.html
Copyright © 2020-2023  润新知