zoukankan      html  css  js  c++  java
  • POj 2104 K-th Number (分桶法+线段树)

    题目链接

    Description

    You are working for Macrohard company in data structures department. After failing your previous task about key insertion you were asked to write a new data structure that would be able to return quickly k-th order statistics in the array segment.
    That is, given an array a[1...n] of different integer numbers, your program must answer a series of questions Q(i, j, k) in the form: "What would be the k-th number in a[i...j] segment, if this segment was sorted?"
    For example, consider the array a = (1, 5, 2, 6, 3, 7, 4). Let the question be Q(2, 5, 3). The segment a[2...5] is (5, 2, 6, 3). If we sort this segment, we get (2, 3, 5, 6), the third number is 5, and therefore the answer to the question is 5.

    Input

    The first line of the input file contains n --- the size of the array, and m --- the number of questions to answer (1 <= n <= 100 000, 1 <= m <= 5 000).
    The second line contains n different integer numbers not exceeding 109 by their absolute values --- the array for which the answers should be given.
    The following m lines contain question descriptions, each description consists of three numbers: i, j, and k (1 <= i <= j <= n, 1 <= k <= j - i + 1) and represents the question Q(i, j, k).

    Output

    For each question output the answer to it --- the k-th number in sorted a[i...j] segment.

    Sample Input

    7 3
    1 5 2 6 3 7 4
    2 5 3
    4 4 1
    1 7 3

    Sample Output

    5
    6
    3

    题意:
    给定一个数列a1,a2,a3,···,an和m个三元组表示的查询。对于每个查询(i,j,k),输出ai,ai+1,···,aj的升序排列中第k个数。

    分析:

    因为查询的个数m很大,朴素的求法没法在规定的时间内求出。

    如果x是第k个数,那么一定有:
    1.在区间中不超过x的数不少于k个
    2.在区间中小于x的数不到k个

    因此,如果可以快速求出区间里不超过x的数的个数,就可以通过对x进行二分搜索求出第k个数是多少。

    接下来,我们来看一下如何计算在某个区间里不超过x的数的个数。如果不进行预处理,那么就只能遍历一遍所有的元素。

    另一方面,如果区间是有序的,那么就可以通过二分搜索法高效地求出不超过x的数的个数了。但是,如果对于每个查询都分别进行一次排序,就完全无法降低复杂度。所以,可以考虑使用平方分割和线段树进行求解。

    首先看一下如何使用平方分割来解决这个问题。把数列每b个一组分到哥哥桶里,每个桶内保存有序后的序列。这样,如果要求在某个区间中不超过x的个数,就可以这样求得:
    1.对于完全包含在区间内的桶,用二分搜索法进行计算
    2.对于所在的桶不完全包含在区间内的元素,逐个检查。

    #include<cstdio>
    #include<iostream>
    #include<string.h>
    #include<vector>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int B = 1072;
    const int maxn = 1e5+5;
    int n, m;
    int a[maxn], num[maxn];///原序列和排序后的序列
    vector<int> buk[maxn/B+1];///每个桶排序后的结果
    
    int main()
    {
    
        scanf("%d%d",&n,&m);
        for(int i = 0; i < n; i++)
        {
            scanf("%d", a+i);
            buk[i/B].push_back(a[i]);
        }
        memcpy(num,a,sizeof(int)*n);
        sort(num,num+n);
        ///(n-1)/b <= n/b 当 n%b != 0时候取等号,这时候不是一个完整的桶,所以最后一个桶不sort也没关系
        for(int i = n/B;i>=0; i--) sort(buk[i].begin(),buk[i].end());
        while(m--)
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            int lb = 0, ub = n-1;
            while(lb < ub)
            {
                int md = (lb+ub)>>1;
                int x = num[md];
                ///求[l,r)区间中第k个数
                int p = l-1, q = r, c = 0;
                ///区间两端多出的部分
                while(p<q && p%B) if(a[p++] <= x) c++;
                while(p<q && q%B) if(a[--q] <= x) c++;
                ///对每一个桶进行计算
                for(int i = p/B, mi = q/B; i < mi; i++)
                {
                    c += upper_bound(buk[i].begin(),buk[i].end(),x)-buk[i].begin();
                }
                c >= k?ub = md: lb = md+1;
            }
            printf("%d
    ", num[lb]);
        }
    
        return 0;
    }
    

    思路同上,只是计算cnt的时候改用线段树。线段树每个结点保存有序的数组,建树过程是归并排序的完整再现,因此也叫归并树。

    更抽象来看,数值大小和位置是两个维度,查询就是询问一个空间内的点数,也就是所谓的区域树了,通过嵌套可以推广到高维。

    #include<cstdio>
    #include<iostream>
    #include<string>
    #include<cstring>
    #include<queue>
    #include<vector>
    #include<stack>
    #include<vector>
    #include<map>
    #include<set>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    #define para int d = 0, int l = 0,int r = n
    #define lsn d+1, l, mid
    #define rsn d+1, mid, r
    #define insd ql<=l&&r<=qr
    const int maxn = 1e5;
    int n, m;
    int a[maxn];
    const int Log2N_ = 18;
    
    int dat[Log2N_][maxn];
    
    void build(para)
    {
        if(r-l == 1)
        {
            dat[d][l] = a[l];
        }
        else
        {
            int mid = (l+r)>>1;
            build(lsn);
            build(rsn);
            merge(dat[d+1]+l,dat[d+1]+mid,dat[d+1]+mid,dat[d+1]+r,dat[d]+l);
        }
    }
    
    int ql, qr, qval;
    int query(para)
    {
        if(insd)
        {
            return upper_bound(dat[d]+l,dat[d]+r,qval) - dat[d]-l;
        } 
        else
        {
            int res = 0;
            int mid = (l+r)>>1;
            if(ql<mid) res += query(lsn);
            if(qr>mid) res += query(rsn);
            return res;
        }
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i = 0; i < n; i++)
        {
            scanf("%d", a+i);
        }
        build();
        while(m--)
        {
            int k;
            scanf("%d%d%d",&ql,&qr,&k);
            ql--;
            int lb = 0, ub = n-1;
            while(lb < ub)
            {
                int md = (lb+ub)>>1;
                qval = dat[0][md];
                query() >= k? ub = md:lb = md+1;
            }
            printf("%d
    ", dat[0][lb]);
        }
    
        return 0;
    }
    
    
  • 相关阅读:
    mongo的csv文件参考
    apache安装配置
    部署Java的运行环境
    ubuntu下没有ping命令
    webbench压力测试
    判断当前是否是微信浏览器,还是APP客户端
    PHP uniqid 高并发生成不重复唯一ID
    http_build_query()函数使用方法
    防XSS攻击
    PHP中json_encode()使用须知,JSON数组和JSON对象
  • 原文地址:https://www.cnblogs.com/cmmdc/p/7230162.html
Copyright © 2011-2022 走看看