分桶法和平方分割(对区间的操作)

来源:互联网 发布:数据库排序 编辑:程序博客网 时间:2024/06/08 18:17

        分桶法:把一排物品或者平面分成桶,每个桶饭分别维护自己的内部信息,以达到高效计算的目的的方法。

        平方分割:把一排n个元素中每√n个元素分在一个桶内进行维护的方法,使对区间操作的时间复杂度降为O(√n)。

        类似线段树哦。


        给定一个n个整数的数列a1, a2, a3, ..., an和m个三元组表示的查询。对于每个查询(i, j, k)输出ai, a(i+1), ..., aj的升序排列中的第k个数。(n <= 100000, m <= 5000)

        输入 n = 7, m = 3      a = {1, 5, 2, 6, 3, 7, 4}   {(2, 5, 3), (4, 4, 1), (1, 7, 3)}

        输出 5 6 3

       

        最容易想到的是把给定的区间进行排序,然后直接求出第k个数,但m<=5000,而且n<=100000,这样会超时的啦。

        可以这样想,如果x为查询区间中的第k个数,那么他会有:

        1)在区间中不超过x的数不少于k个(有可能区间中有多个x)

        2)在区间中小于x的数有不到k个

        所以可以使用二分来对排好序的b[]数列(对a[]排好序后的数组)进行查找x是否满足上面的条件,直到找到完成查找哦。

        判断x是否满足:如果查询区间都落在一个桶的范围上,那么就拿x和a[i]到a[j]一个个比较,记录k的值; 如果查询区间在多个桶之间,那么第一个桶和最后一个桶只需遍历,其它的直接使用upper_bound(其它的桶里的元素都排好序了)。

#include <stdio.h>#include <vector>#include <algorithm>using namespace std;#define MAX_N 100005const int BUCKETS_SIZE = 1000;int a[MAX_N], b[MAX_N];int s[5005], e[5005], pos[5005];vector<int> buckets[MAX_N / BUCKETS_SIZE];int n, m;int main() {int i;int l, r, k, tl, tr, tk;int left, right, mid;int x, tmp;while (scanf("%d%d", &n, &m) != EOF && n || m) {for (i = 0; i <= n / BUCKETS_SIZE; i++) {buckets[i].clear();}// 输入数列并入桶for (i = 1; i <= n; i++) {scanf("%d", &a[i]);buckets[i / BUCKETS_SIZE].push_back(a[i]);b[i] = a[i];}// 输入三元组for (i = 0; i < m; i++) {scanf("%d%d%d", &s[i], &e[i], &pos[i]);}// 排序,剩下一个桶可以不排序sort(b + 1, b + n + 1);for (i = 0; i < n / BUCKETS_SIZE; i++) {sort(buckets[i].begin(), buckets[i].end());} for (i = 0; i < m; i++) {l = s[i], r = e[i], k = pos[i];left = 1, right = n + 1;while (left < right) {mid = (left + right) / 2;x = b[mid];tl = l, tr = r, tk = 0;// 在同一个桶中,遍历区间while (tl <= tr && tl % BUCKETS_SIZE != 0) {if (a[tl++] <= x) {++tk;}}while (tl <= tr && tr % BUCKETS_SIZE != 0) {if (a[tr--] <= x) {++tk;}}// 区间不全在一个桶中,可以从排好序的同桶中upper_bound取得while (tl <= tr) {tmp = tl / BUCKETS_SIZE;tk += upper_bound(buckets[tmp].begin(), buckets[tmp].end(), x) - buckets[tmp].begin();tl += BUCKETS_SIZE;}if (k <= tk) {right = mid;} else {left = mid + 1;}}printf("%d\n", b[right]);}}return 0;}

7 31 5 2 6 3 7 42 5 34 4 11 7 37 49 3 7 4 5 3 81 7 71 7 41 7 51 7 37 49 3 7 4 5 3 82 5 27 7 13 6 21 7 7


原创粉丝点击