POJ 2104 K-th Number
2013-10-23 23:09
369 查看
刚开始接触线段树,然后碰到这道题用“线段树”的方式去思考没想明白,然后看了一篇题解恍然大悟,方法果然高明
题目描述:
给定数组a
(n < 100,000),做m次查询(m < 5,000),查询内容为[a, b]内第k大的数
如果对每一次查询都先排序的话肯定会超时(mnlogn),既然数组是给定了的,我们可不可以做一些预处理呢?
从有序的数组中找第k大的数是容易的,但是我们显然无法对每一段数组都先进行排序,这样空间是承受不起的
如果查询的区间越大排序所需要的时间越长,如果可以把这个区间缩小的话查找就容易多了
既然如此的话我们可不可以让这个数组大体有序?
通过二分的思想,我们可以很容易的做到让这个数组的前一半比后一半小,可以构造这样一个预处理的二维数组,每一层都是上一层子区间二分后的结果,所需空间为maxLength*log(maxLength)
接下来查询时要做的就是把某一层[a, b]内第k大的数转换为下一层的[a1, b1]找第k1个数,这样每一次搜索的范围都减半,每次查询的时间便是logn了
下面的问题就是如何把[a, b]转换为[a1, b1],k如何转换为k1了
上面说要将一个区间分成两个,前一半比后一半小,首先要知道的是第k个数应该到哪个区间去找,记中位数为mid,要看的就是第k个数比mid大还是小了,如果查询区间内的数中有t个被塞入了前半个区间(<mid),这t个数是查询区间中的前t小个数,那么如果k在这t个之中,那么显然k < mid,应该到前半个区间去找,所以查找的区间由k和t的大小关系决定
于是t该怎么算?在预处理的时候另开一个数组left[i]记录截止到i为止有多少个点比mid小被扔进了前半个数组就好了,这样[a, b]内的t = left[b] - left[a - 1](注意a = 区间边界的情况)
在向下一个区间查找的转移过程中,应保证区间中元素的相对位置不变,这样才能保证查询区间不会混入其他的元素,这个比较好处理,分区间时顺序的扫一遍再由mid筛选自然就形成了
最后当查询的区间长度变为1的时候,那个值自然就是我们想要的了
下边举一个预处理二维数组的例子
a
1
5 2 6
3 7
4 mid=5
depth=1 1
2 3 |
5 6 7
4 mid=2 | mid = 6
depth=2 1 | 2
3 | 5
4 | 6
7
depth=3 1 |
2 | 3 |
4 | 5 |
6 | 7
不难发现到最后的时候数组就是完全排序的了
下面是源码,写的比较挫==也没加注释,以后有时间改....
题目描述:
给定数组a
(n < 100,000),做m次查询(m < 5,000),查询内容为[a, b]内第k大的数
如果对每一次查询都先排序的话肯定会超时(mnlogn),既然数组是给定了的,我们可不可以做一些预处理呢?
从有序的数组中找第k大的数是容易的,但是我们显然无法对每一段数组都先进行排序,这样空间是承受不起的
如果查询的区间越大排序所需要的时间越长,如果可以把这个区间缩小的话查找就容易多了
既然如此的话我们可不可以让这个数组大体有序?
通过二分的思想,我们可以很容易的做到让这个数组的前一半比后一半小,可以构造这样一个预处理的二维数组,每一层都是上一层子区间二分后的结果,所需空间为maxLength*log(maxLength)
接下来查询时要做的就是把某一层[a, b]内第k大的数转换为下一层的[a1, b1]找第k1个数,这样每一次搜索的范围都减半,每次查询的时间便是logn了
下面的问题就是如何把[a, b]转换为[a1, b1],k如何转换为k1了
上面说要将一个区间分成两个,前一半比后一半小,首先要知道的是第k个数应该到哪个区间去找,记中位数为mid,要看的就是第k个数比mid大还是小了,如果查询区间内的数中有t个被塞入了前半个区间(<mid),这t个数是查询区间中的前t小个数,那么如果k在这t个之中,那么显然k < mid,应该到前半个区间去找,所以查找的区间由k和t的大小关系决定
于是t该怎么算?在预处理的时候另开一个数组left[i]记录截止到i为止有多少个点比mid小被扔进了前半个数组就好了,这样[a, b]内的t = left[b] - left[a - 1](注意a = 区间边界的情况)
在向下一个区间查找的转移过程中,应保证区间中元素的相对位置不变,这样才能保证查询区间不会混入其他的元素,这个比较好处理,分区间时顺序的扫一遍再由mid筛选自然就形成了
最后当查询的区间长度变为1的时候,那个值自然就是我们想要的了
下边举一个预处理二维数组的例子
a
1
5 2 6
3 7
4 mid=5
depth=1 1
2 3 |
5 6 7
4 mid=2 | mid = 6
depth=2 1 | 2
3 | 5
4 | 6
7
depth=3 1 |
2 | 3 |
4 | 5 |
6 | 7
不难发现到最后的时候数组就是完全排序的了
下面是源码,写的比较挫==也没加注释,以后有时间改....
#include <iostream> #include <cstring> #include <cstdio> #include <cstdlib> #define MAXN 100010 int a[21][MAXN],left[21][MAXN]; struct treeNode { int s; int e; int mid; int depth; }tree[MAXN * 3]; int cmp(const void * a, const void * b) { return *(const int *)a - *(const int *)b; } void buildTree(int s, int e, int depth, int tr) { tree.s = s; tree .e = e; tree .depth = depth; tree .mid = (s + e) >> 1; if (s >= e - 1) return; int mid = (e + s) >> 1; int t = a[20][mid]; int i = s, j = mid; for (int k = s; k < e; k++) { if (a[depth][k] < t) a[depth + 1][i++] = a[depth][k]; else a[depth + 1][j++] = a[depth][k]; left[depth][k] = i - s; } buildTree(s, mid, depth + 1, 2 * tr + 1); buildTree(mid, e, depth + 1, 2 * tr + 2); return; } int query(int s, int e, int k, int pTree) { treeNode & tr = tree[pTree]; if (s == e - 1) return a[tr.depth][s]; if (s == tr.s && e == tr.e) return a[20][tr.s + k - 1]; int f = (s == tr.s ? 0 : left[tr.depth][s - 1]); int t = left[tr.depth][e - 1] - f; if (k <= t) return query(tr.s + (s == tr.s ? 0 : left[tr.depth][s - 1]), tr.s + left[tr.depth][e - 1], k, pTree * 2 + 1); else return query(tr.mid + (s - tr.s - f), tr.mid + (s - tr.s - f) + e - s - t, k - t, pTree * 2 + 2); } int main() { int n, m, s, e ,k; scanf("%d%d", &n, &m); for (int i = 0; i < n; ++i) { scanf("%d", &a[0][i]); } memcpy(a[20], a[0], sizeof(int) * n); // 用a[20]来记录完全排序的结果 qsort(a[20], n, sizeof(int), cmp); buildTree(0, n, 0, 0); for (int i = 0; i < m; ++i) { scanf("%d%d%d", &s, &e, &k); printf("%d\n", query(s - 1, e, k, 0)); } return 0; } 相关文章推荐
- poj 2104 K-th Number 主席树+超级详细解释
- POJ 2104 K-th Number(划分树)
- POJ_2104_K-th Number_线段树(归并树)
- POJ 2104 K-th Number (区间第k大)
- K-th Number(poj 2104)
- POJ 2104 K-th Number (可持久化线段树)
- [POJ 2104]K-th Number
- poj 2104 K-th Number
- POJ 2104 K-th Number——分块平方分割
- 【poj 2104】K-th Number【整体二分+树状数组】
- POJ-2104-K-th Number(函数式线段树)
- 【POJ - 2104 】K-th Number 【分块 (平方分割)+二分】
- poj 2104 K-th number (模板级划分树)
- POJ2104_K-th Number_归并树|二分 ||平方分割的分桶法|二分
- POJ-2104:K-th Number(主席树)
- K-th Number - POJ 2104 划分树
- poj 2104 K-th Number 划分树
- poj 2104 K-th Number 函数式线段树
- poj 2104 K-th Number
- 【转】POJ 2104 K-th Number(2)