zoukankan      html  css  js  c++  java
  • poj 2104 K-th Number

                                                                                                                                                                    K-th Number
    Time Limit: 20000MS   Memory Limit: 65536K
    Total Submissions: 55340   Accepted: 19030
    Case Time Limit: 2000MS

    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

    Hint

    This problem has huge input,so please use c-style input(scanf,printf),or you may got time limit exceed.
     
    题意:给定一个数列a1,a2,...,an,和一个数m,m代表查询次数,对于每一次查询操作,会给出(i,j,k),即要你查询ai到aj之间从小到大排第k个数是多少,一共查询m次。
    思路:挑战程序设计竞赛中的原题,按书上做法有两种,一种是桶分法,其目的是若给定一个数x,找到在区间[i,j]中小于等于x的元素个数,这样就可以进行二分搜索查找出需要查询的那个数。桶分法做法:即将元素b(b的大小自己设定)个一组放进不同的桶中,每个桶都已进行排序,查询的时候若查询区间完全覆盖一个桶,则可以二分搜索出这个桶中小于等于当前元素x的元素个数,若不完全覆盖,则一个一个元素拿出来比较,是否小于等于x。累加利用桶分法找到的所有小于等于x的元素个数,这样即可进行二分搜索找到合适的x。但桶分法的做法提交会TLE,所以还是利用线段树,利用线段树来找到所有小于等于当前元素x的值的个数会比较效率一些,线段树的每一个节点维护的是一个排好序的数列,递归查找即可,接下来的二分搜索找合适x的过程还是一样的。
    AC代码:
    #define _CRT_SECURE_NO_DEPRECATE
    #include<iostream>
    #include<algorithm>
    #include<vector>
    using namespace std;
    const int N_MAX = 100000 + 4,M_MAX=5000+1;
    const int ST_SIZE = (1<<18)-1;
    int N, M;
    int A[N_MAX];
    int l[M_MAX], r[M_MAX], k[M_MAX];
    int nums[N_MAX];//nums是排好序的数列,对于其中每一个数进行二分搜索,判断是否符合要求
    vector<int>dat[ST_SIZE];
    
    void init(int k,int l,int r) {//节点k,对应区间[l,r)
        if (r - l == 1) {
            dat[k].push_back(A[l]);//节点处的数列只有一个值
        }
        else {
            int k1 = 2 * k + 1, k2 = 2 * k + 2;
            init(k1,l,(l+r)/2);
            init(k2,(l+r)/2,r);
            dat[k].resize(r-l);//先预置空间,才可以进行归并
            merge(dat[k1].begin(), dat[k1].end(),dat[k2].begin(),dat[k2].end(),dat[k].begin());
        }
    }
    
    int query(int a,int b,int x,int k,int l,int r) {//查找区间[a,b)中小于等于x的数的个数
        if (l >= b || r <= a) {
            return 0;
        }
        else if (a <= l&&r <= b) {
            return upper_bound(dat[k].begin(),dat[k].end(),x)-dat[k].begin();
        }
        else {
            int lc = query(a, b, x, 2 * k + 1, l, (l + r) / 2);
            int rc = query(a, b, x, 2 * k + 2, (l + r) / 2, r);
            return lc + rc;
        }
    }
    
    
    int main() {
        scanf("%d%d",&N,&M);
        for (int i = 0; i < N; i++) {
            scanf("%d",&A[i]);
            nums[i] = A[i];
        }
        sort(nums,nums+N);
        for (int i = 0; i < M; i++) {
            scanf("%d%d%d",&l[i],&r[i],&k[i]);
            l[i]--, r[i]--;
        }
        init(0,0,N);
        
        for (int i = 0; i < M; i++) {
            int L = l[i], R = r[i]+1, K = k[i];//!!!!!+1别忘
            int lb = -1, ub = N - 1;
            while (ub - lb > 1) {
                int mid = (lb + ub) >> 1;
                int kk = query(L, R, nums[mid], 0, 0, N);
                if (kk >= K)ub = mid;
                else lb = mid;
            }
            printf("%d
    ",nums[ub]);
        }
        return 0;
    }
  • 相关阅读:
    python之enumerate
    Python中的集合set
    SGU 分类
    太空飞行计划 最大权闭合图
    1.飞行员配对 二分图匹配(输出方案)/最大流斩
    poj1149最大流经典构图神题
    hdu1569 方格取数 求最大点权独立集
    最大独立集 最小点覆盖 最小边覆盖 最小路径覆盖 最大团
    hdu3491最小割转最大流+拆点
    hdu3987,最小割时求最少割边数
  • 原文地址:https://www.cnblogs.com/ZefengYao/p/6655358.html
Copyright © 2011-2022 走看看