The Famous ICPC Team Again

时间限制: 5 Sec 内存限制: 128 MB

题目描述
When Mr. B, Mr. G and Mr. M were preparing for the 2012 ACM-ICPC World Final Contest, Mr. B had collected a large set of contest problems for their daily training. When they decided to take training, Mr. B would choose one of them from the problem set. All the problems in the problem set had been sorted by their time of publish. Each time Prof. S, their coach, would tell them to choose one problem published within a particular time interval. That is to say, if problems had been sorted in a line, each time they would choose one of them from a specified segment of the line.

Moreover, when collecting the problems, Mr. B had also known an estimation of each problem’s difficultness.
When he was asked to choose a problem, if he chose the easiest one, Mr. G would complain that “Hey, what a trivial problem!”; if he chose the hardest one, Mr. M would grumble that it took too much time to finish it. To address this dilemma, Mr. B decided to take the one with the medium difficulty. Therefore, he needed a way to know the median number in the given interval of the sequence.
输入
For each test case, the first line contains a single integer n (1 <= n <= 100,000) indicating the total number of problems. The second line contains n integers xi (0 <= xi <= 1,000,000,000), separated by single space, denoting the difficultness of each problem, already sorted by publish time. The next line contains a single integer m (1 <= m <= 100,000), specifying number of queries. Then m lines follow, each line contains a pair of integers, A and B (1 <= A <= B <= n), denoting that Mr. B needed to choose a problem between positions A and B (inclusively, positions are counted from 1). It is guaranteed that the number of items between A and B is odd.
输出
For each query, output a single line containing an integer that denotes the difficultness of the problem that Mr. B should choose.
样例输入 Copy
5
5 3 2 4 1
3
1 3
2 4
3 5
5
10 6 4 8 2
3
1 3
2 4
3 5
样例输出 Copy
Case 1:
3
3
2
Case 2:
6
6
4
题目意思:
开始给出 n 个数,然后有若干次询问,每次给出一个区间,要求解出给定的区间里面的中位数是多少,输入的数据中保证这个区间里面有奇数个数
题目考点是划分树,据学长说 主席树也能够解决这一类问题

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <stack>
#include <map>
#include <set>
#include <cmath>
#include <math.h>
#include <string.h>
 
typedef int itn;
using namespace std;
typedef long long ll;
const int maxn = 1e5+7;
const int mod = 1e9+7;
 
inline ll read() {
    ll c = getchar(), Nig = 1, x = 0;
    while (!isdigit(c) && c != '-')c = getchar();
    if (c == '-'){
        Nig = -1;
        c = getchar();
    }
    while (isdigit(c))x = ((x << 1) + (x << 3)) + (c ^ '0'), c = getchar();
    return Nig * x;
}
///typedef read() read;
#define read read()
int maxx = -1;
int minn = 0x3f3f3f3f;
 
int a[40][maxn];
int b[maxn];
int cnt[40][maxn];/// cnt[a][b]表示第a层,从1到b有多少个数分到左边
 
void build(int l,int r,int flor){
    /// 左右区间和层数
    if(l == r) return ;/// 左边和右边相等直接返回
    int mid = (l + r) / 2;/// 左右两个端点求个中间值
    int equ_mid = (mid - l + 1);/// 等于中间的那个数的个数
    for(int i = l;i <= r; i ++){
        if(a[flor][i] < b[mid]) equ_mid --;/// 不等于中间的那个数,对应数量减小 1
    }
    int t_l = l;/// 左指针
    int t_r = mid + 1;/// 标记右指针
    /**
    关于为什么是 mid + 1是因为,如果出现右面的,先对中间偏右面的数进行修改,然后在++
    来更新值
    **/
 
    for(int i = l;i <= r;i ++){
        /// 当前这个数比中间的数小
        if(a[flor][i] < b[mid]) a[flor + 1][t_l ++] = a[flor][i];
        /// 等于中间的数,并且等于中间值的数量不是 0,是为了避免不出现中间的值记录两次的情况
        else if(a[flor][i] == b[mid] && equ_mid){
            a[flor + 1][t_l ++] = a[flor][i];
            equ_mid --;/// 数量减小
        }/// 大小大于中间值的数被分进右面
        else{
            a[flor + 1][t_r ++] = a[flor][i];
        }
        cnt[flor][i] = cnt[flor][l - 1] + t_l - l;
    }
    build(l ,mid ,flor + 1);/// 递归建树 左面
    build(mid + 1 , r , flor + 1);/// 递归建树 右面
 
}
 
int _findAns(int L,int R,int l,int r,int flor,int K){
    /// 在大区间 L 到 R 的范围内,寻找小区间 l 到 r 之内在第 flor 层寻找第 K 大的数
    if(l == r) return a[flor][r];///左右相等直接返回
    int mid = (L + R) / 2;
    int num = cnt[flor][r] - cnt[flor][l - 1];
    /// 中间的数量等于右边的数量减去 posl-1的位置减一的数量,相当于前缀和
    if(num >= K) ///数量大于 k
    {
        int n_l = L + cnt[flor][l - 1] - cnt[flor][L - 1];
        int n_r = n_l + num - 1;
        return _findAns(L,mid,n_l,n_r,flor + 1,K);
    }
    else{
        int n_r = r + cnt[flor][R] - cnt[flor][r];
        int n_l = n_r - (r - l - num);/// 根据右端点更新左端点
        return _findAns(mid + 1,R,n_l,n_r,flor + 1,K - num);
    }
}
int main(){
    int n; int cnt = 0;
    while(cin >> n){
        memset(a,0,sizeof(a));
        for(int i = 1;i <= n; i ++){
            a[0][i] = read;
            b[i] = a[0][i];/// 拷贝数组元素
        }/// 然后进行排序
        sort(b + 1,b + 1 + n);
        build(1 , n , 0);/// 从1 -> n之内进行建树
        printf("Case %d:
",++cnt);
        int q = read;
        while(q--){
            int l=read,r=read;
            int k = (r - l) / 2 + 1;
            printf("%d
",_findAns(1,n,l,r,0,k));
        }
    }
    return 0;
}
/**
5
5 3 2 4 1
3
1 3
2 4
3 5
5
10 6 4 8 2
3
1 3
2 4
3 5
 
Case 1:
3
3
2
Case 2:
6
6
4
**/
 
/**************************************************************
    Problem: 10968
    Language: C++
    Result: 正确
    Time:595 ms
    Memory:33668 kb
****************************************************************/
原文地址:https://www.cnblogs.com/PushyTao/p/13675760.html