树状数组快速寻找getsum(i) < k的最大的 i
2014-08-17 18:38
253 查看
如果是线段树来做这个操作,很明显一个query下去就可以,复杂度O(logn);
而如果是树状数组来做的话,很容易想到的就是二分查找了,如下
二分的复杂度是O(logn),getsum的复杂度O(logn)。两个一叠起来,肯定是打不过线段树的O(logn)。那么有没有办法降低复杂度,不那么频繁的调用getsum呢?
不要忘了树状数组的英文名是Binary Indexed Tree,二进制索引树,我们可以考虑从二进制入手分析。
我们先来看getsum函数,假设参数x是90,其二进制表示为1011010。getsum的求和过程是bit[1011010] + bit[1011000] + bit[1010000] + bit[1000000](未方便说明,[]使用的是二进制);
如果把这个过程反过来,改为bit[1000000] + bit[1010000] + bit[1011000] + bit[1011010],根据树状数组的性质。我们有bit[1<<i] > bit[1011010](when i > 6)(这个很容易看出来)。这个说明我们从高位到低位枚举二进制位数的时候,可以很容易的确定所要找的数的第一个1的位置。
那么第二1的位置呢?这时由于bit[1<<6 | 1<<i](i<6)代表的数是从(1<<6)+1~1<<6|1<<i的数的和。所以仍然有bit[1<<6|1<<i] < bit[1<<6|1<<j] (i<j)。说明我们在枚举的过程中也可以确定第二个1的位置。
剩余的1分析同理。也就是说,我们可以在一个从高位到低位枚举二进制位数的过程中找到我们所要的答案。这个情况下,每个位置都只要做一个if判断和几个加减法,复杂度O(logn),这样我们就有了能打过线段树的写法。如下
和这个有关的题目有POJ 2828,这个题用正常的二分查找应该是可以过的,然后BUPT
OJ 494(BUPT oj需要登录才可以看见题)与poj的题题意相同,但是数据较强,正常的二分查找会TLE,要加读入优化等才可以擦边过。SGU 311也是一个可以用此方法使用树状数组的题目。
而如果是树状数组来做的话,很容易想到的就是二分查找了,如下
int find(int sum) { int l = 1, r = MAX_N, m, ans = 0; while(l <= r) { m = (l+r)>>1; if(getsum(m) < sum) ans = m, l = m+1; else r = m-1; } return ans; }
二分的复杂度是O(logn),getsum的复杂度O(logn)。两个一叠起来,肯定是打不过线段树的O(logn)。那么有没有办法降低复杂度,不那么频繁的调用getsum呢?
不要忘了树状数组的英文名是Binary Indexed Tree,二进制索引树,我们可以考虑从二进制入手分析。
我们先来看getsum函数,假设参数x是90,其二进制表示为1011010。getsum的求和过程是bit[1011010] + bit[1011000] + bit[1010000] + bit[1000000](未方便说明,[]使用的是二进制);
如果把这个过程反过来,改为bit[1000000] + bit[1010000] + bit[1011000] + bit[1011010],根据树状数组的性质。我们有bit[1<<i] > bit[1011010](when i > 6)(这个很容易看出来)。这个说明我们从高位到低位枚举二进制位数的时候,可以很容易的确定所要找的数的第一个1的位置。
那么第二1的位置呢?这时由于bit[1<<6 | 1<<i](i<6)代表的数是从(1<<6)+1~1<<6|1<<i的数的和。所以仍然有bit[1<<6|1<<i] < bit[1<<6|1<<j] (i<j)。说明我们在枚举的过程中也可以确定第二个1的位置。
剩余的1分析同理。也就是说,我们可以在一个从高位到低位枚举二进制位数的过程中找到我们所要的答案。这个情况下,每个位置都只要做一个if判断和几个加减法,复杂度O(logn),这样我们就有了能打过线段树的写法。如下
int getPos(int sumToFind) { int pos = 0, nowSum = 0, k = 0; while(1<<k < MAX_N) k++; for(int i = k; i >= 0; i--) { pos += 1<<i; if(pos > MAX_N || nowSum + bit[pos] >= sumToFind) pos -= 1<<i; else nowSum += bit[pos]; } return pos; }
和这个有关的题目有POJ 2828,这个题用正常的二分查找应该是可以过的,然后BUPT
OJ 494(BUPT oj需要登录才可以看见题)与poj的题题意相同,但是数据较强,正常的二分查找会TLE,要加读入优化等才可以擦边过。SGU 311也是一个可以用此方法使用树状数组的题目。
相关文章推荐
- java快速寻找一个数组的最大值或最小值, min, max,三种方法
- 在一个整数数组中寻找符合A+B=C的组合,使C为最大
- 寻找数组中的最大值和最小值(编程之美2.10,包括最大值和次大值)
- 【100题】第六十一题~第六十五题(数组中只出现一次的数、链表公共点、删除字串特定字符、寻找丑数、输出从1到最大的N 位数)
- 给一个数组,元素都是整数(有正数也有负数),寻找连续的元素相加之和为最大的序列。
- 寻找数组中的最大值和最小值(编程之美2.10)
- 寻找数组中的最大递增序列
- 同时寻找一个数组中的最大元素和最小元素--你会有所收获
- 编程之美_2.10_寻找数组中的最大值和最小值
- 寻找数组中的最大值和最小值(编程之美2.10,包括最大值和次大值)
- 寻找数组中的最小值和最大值——编程之美2.10
- 读书笔记之编程之美 - 2.10 寻找数组中的最大值和最小值
- 寻找数组中最大值和最小值
- 一维静态数组_实例:寻找随机数最大值及其下标(附:源码)
- 2.10 寻找数组中的最大值和最小值
- 给一个数组,求a[i]<a[j]的情况下,j-i的最大值是多少,要求用O(n)时间复杂度
- 寻找在数组中一个元素是不是该行最小,该列最大的元素
- 求子数组的最大和___<3>
- 2.10 寻找数组中的最大值和最小值
- 快速找出一个数组中的最大数、第二大数