vijos - P1081野生动物园 (划分树 + 线段树)

P1081野生动物园
标签: [显示标签]

描述

cjBBteam拥有一个很大的野生动物园。这个动物园坐落在一个狭长的山谷内,这个区域从南到北被划分成N个区域,每个区域都饲养着一头狮子。这些狮子从北到南编号为1,2,3,…,N。每头狮子都有一个觅食能力值Ai,Ai越小觅食能力越强。饲养员cmdButtons决定对狮子进行M次投喂,每次投喂都选择一个区间[I,J],从中选取觅食能力值第K强的狮子进行投喂。值得注意的是,cmdButtons不愿意对某些区域进行过多的投喂,他认为这样有悖公平。因此cmdButtons的投喂区间是互不包含的。你的任务就是算出每次投喂后,食物被哪头狮子吃掉了。

格式

输入格式

输入第一行有两个数N和M。此后一行有N个数,从南到北描述狮子的觅食能力值。此后M行,每行描述一次投喂。第t+2的三个数I,J,K表示在第t次投喂中,cmdButtons选择了区间[I,J]内觅食能力值第K强的狮子进行投喂。

输出格式

输出有M行,每行一个整数。第i行的整数表示在第i次投喂中吃到食物的狮子的觅食能力值。

样例1

样例输入1[复制]

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

样例输出1[复制]

3
2

限制

各个测试点2s

提示

对于100%的数据,有1<=N<=100000,1<=M<=50000。

来源

周戈林


#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <queue>
#include <vector>
#include <cctype>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;


#define pb push_back
#define mp make_pair
#define fillchar(a, x) memset(a, x, sizeof(a))
#define copy(a, b) memcpy(a, b, sizeof(a))


typedef long long LL;
typedef pair<int, int > PII;
typedef unsigned long long uLL;
template<typename T>
void print(T* p, T* q, string Gap = " ") {
    int d = p < q ? 1 : -1;
    while(p != q) {
        cout << *p;
        p += d;
        if(p != q) cout << Gap;
    }
    cout << endl;
}
template<typename T>
void print(const T &a, string bes = "") {
    int len = bes.length();
    if(len >= 2)cout << bes[0] << a << bes[1] << endl;
    else cout << a << endl;
}

const int INF = 0x3f3f3f3f;
const int MAXM = 1e5;
const int MAXN = 1e5 + 5;

LL sum[20][MAXN];
int tree[20][MAXN], sorted[MAXN];
int N, M, I, J, K;

void build(int rt, int l, int r) {
    int mid = (l + r) >> 1, p = l, q = mid + 1, same = mid - l + 1;//得到应该平分为二的大小
    for(int i = l; i <= mid ; i ++) {//小于的话先去掉
        if(sorted[i] < sorted[mid]) same --;
    }
    for(int i = l; i <= r; i ++) {
        if(i == l) {
            sum[rt][i] = 0;
        } else {
            sum[rt][i] = sum[rt][i - 1];
        }
        if(tree[rt][i] == sorted[mid]) {
            if(same) {//如果相同就需要特殊判断相同的数字分在左边还是右边
                same --;
                sum[rt][i] ++;
                tree[rt + 1][p ++] = tree[rt][i];
            } else {
                tree[rt + 1][q ++] = tree[rt][i];
            }
        } else if(tree[rt][i] < sorted[mid]) {
            sum[rt][i] ++;
            tree[rt + 1][p ++] = tree[rt][i];
        } else {
            tree[rt + 1][q ++] = tree[rt][i];
        }
    }
    if(l == r) return;
    build(rt + 1, l, mid);
    build(rt + 1, mid + 1, r);
}

int query(int rt,int l, int r, int L, int R, int k) {
    int s, ss, mid = (l + r) >> 1;
    if(l == r) return tree[rt][l];
    if(l == L) {
        s = 0, ss = sum[rt][R];
    } else {
        s = sum[rt][L - 1];//得到[l, L - 1]在左边的大小
        ss = sum[rt][R] - s;//得到[L, R]在左边的大小
    }
    if(k <= ss) {
        return query(rt + 1, l, mid, l + s, l + s + ss - 1, k);//如果分在左边的话,取l + 左边的数
    } else {
        return query(rt + 1, mid + 1, r, mid + 1 + L - l - s, mid + 1 + R - l - s - ss, k - ss);//如果分在右边的话取mid + 1 + 分在右边的数
    }
}

int main() {
    while(cin >> N >> M) {
        for(int i = 1 ; i <= N ; i ++) {
            cin >> sorted[i];
            tree[0][i] = sorted[i];
        }
        sort(sorted + 1, sorted + N + 1);
        build(0, 1, N);
        while(M --) {
            cin >> I >> J >> K;
            print(query(0, 1, N, I, J, K));
        }
    }
    return 0;
}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值