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)<<"\n";
63   }
64   return 0;
65 }

 树状数组

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

所以就顺便发表在博客里吧,等到过一段时间,真正明白其原理了,再回来修改。

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

首先我们需要两个数组,一个是原数组a,一个是树状数组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\n",kk-zz);
37         }
38     }
39     return 0;
40 }

原文地址:https://www.cnblogs.com/wxyww/p/8992279.html

时间: 2024-10-02 15:51:29

ST表与树状数组的相关文章

poj2299(Ultra-QuickSort)树状数组+离散化

题目就是让你求逆序数,用树状数组很简单,不过数据太大,要先进行离散化,将数据范围压缩到1~n以内.还有poj竟然不支持c++11,害得我lambda表达式编译错误. #include <iostream> #include <sstream> #include <fstream> #include <string> #include <map> #include <vector> #include <list> #incl

hdu4777 Rabbit Kingdom 树状数组+区间操作+素数打表

题目大意:给N个数,有M个查询,问区间[L,R]之间有多少个数与这个区间内的其他数都互质. 思路:dp显然很难搞,既然是求区间就试试每一个数最大可以对答案产生贡献的区间,即预处理出一个数在(lp,rp)内始终与其他数互质的最大区间 则lp和rp分别为左边和右边第一个与他不互质的数的位置 处理的时候素数打表然后从左到右始终更新对于某一个素因子出现的最右的位置然后更新l,r,可以做到 然后就是把查询按l从小到大排序,这样的话每处理一个新的查询,对于在这个查询的 l 左边的数就可以不用考虑了 然后我们

[LuoguP4094] [HEOI2016] [TJOI2016]字符串(二分答案+后缀数组+ST表+主席树)

[LuoguP4094] [HEOI2016] [TJOI2016]字符串(二分答案+后缀数组+ST表+主席树) 题面 给出一个长度为\(n\)的字符串\(s\),以及\(m\)组询问.每个询问是一个四元组\((a,b,c,d)\),问\(s[a,b]\)的所有子串和字符串\(s[c,d]\)的最长公共前缀长度的最大值. \(n,m \leq 10^5\) 分析 显然答案有单调性.首先我们二分答案\(mid\),考虑如何判定. 如果mid这个答案可行,那么一定存在一个后缀x,它的开头在\([a,

hdu 4777 Rabbit Kingdom(离线树状数组&amp;思维)

Rabbit Kingdom Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 964    Accepted Submission(s): 315 Problem Description Long long ago, there was an ancient rabbit kingdom in the forest. Every rab

(DP ST表 线段树)51NOD 1174 区间中最大的数

给出一个有N个数的序列,编号0 - N - 1.进行Q次查询,查询编号i至j的所有数中,最大的数是多少. 例如: 1 7 6 3 1.i = 1, j = 3,对应的数为7 6 3,最大的数为7.(该问题也被称为RMQ问题) Input 第1行:1个数N,表示序列的长度.(2 <= N <= 10000) 第2 - N + 1行:每行1个数,对应序列中的元素.(0 <= S[i] <= 10^9) 第N + 2行:1个数Q,表示查询的数量.(2 <= Q <= 1000

树状数组(下)

树状数组(下) 目录 树状数组(下) 应用 逆序对 康托展开 逆康托展开 RMQ问题树状数组解法 查询第k小 习题 Preprefix sum 在树状数组(上)中我提到了树状数组的基本操作与变式,现在来看看它的实际应用和一些题目. 应用 逆序对 设\(a\)为一个有\(n\)个数字的有序集(\(n>1\)),其中所有数字各不相同. 如果存在正整数\(i\),\(j\)使得\(1\leqslant i<j\leqslant n\)且\(a[i]>a[j]\), 则有序对\((a[i],a[

Vijos P1066 弱弱的战壕【多解,线段树,暴力,树状数组】

弱弱的战壕 描述 永恒和mx正在玩一个即时战略游戏,名字嘛~~~~~~恕本人记性不好,忘了-_-b. mx在他的基地附近建立了n个战壕,每个战壕都是一个独立的作战单位,射程可以达到无限(“mx不赢定了?!?”永恒[email protected][email protected]). 但是,战壕有一个弱点,就是只能攻击它的左下方,说白了就是横纵坐标都不大于它的点(mx:“我的战壕为什么这么菜”ToT).这样,永恒就可以从别的地方进攻摧毁战壕,从而消灭mx的部队. 战壕都有一个保护范围,同它的攻击

【初识——树状数组】 区间求最值

说树状数组其实是一个索引表,但是是一个特殊的,树状的索引表,它利用了二进制的一些特性. 就区间求和的要求来说: 首先我们用a[]数组来存储原始数据.然后在a[]之上构造c[]数组来作为树状数组. 如图 这个图表示,当i为奇数时,c[i]中保存的都是a[i]本身.然后,c[2]中保存了a[1], a[2],共2个,c[4]中保存的是a[1], a[2], a[3], a[4],c[6]又是保存两个,c[5]和c[6].c[8]保存8个,c[1], c[2], c[3], c[4], c[5], c

浅谈二维中的树状数组与线段树

一般来说,树状数组可以实现的东西线段树均可胜任,实际应用中也是如此.但是在二维中,线段树的操作变得太过复杂,更新子矩阵时第一维的lazy标记更是麻烦到不行. 但是树状数组在某些询问中又无法胜任,如最值等不符合区间减法的询问.此时就需要根据线段树与树状数组的优缺点来选择了. 做一下基本操作的对比,如下图. 因为线段树为自上向下更新,从而可以使用lazy标记使得矩阵的更新变的高校起来,几个不足就是代码长,代码长和代码长. 对于将将矩阵内元素变为某个值,因为树状数组自下向上更新,且要满足区间加法等限制