标签:
| Time Limit: 20000MS | Memory Limit: 65536K | |
| Total Submissions: 46589 | Accepted: 15553 | |
| Case Time Limit: 2000MS | ||
Description
Input
Output
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
题意:
给定一个数列a1,a2,…,an和m个三元组表示的查询。对于每个查询(i,j,k),输出ai,ai+1,…,aj的升序排列中第k个数。
分析:
使用线段树来解决这个问题。我们把数列用线段树维护起来。线段树的每个节点都保存了对应区间排好序的结果。在这之前我们接触过的线段树节点上保存的都是数值,而这次则有所不同,每个节点保存了一个数列。
建立线段树的过程和归并排序的类似,而每个节点的数列就是其两个儿子节点的数列合并后的结果。建树的复杂度是O(logn)。顺便一提,这棵树正是归并排序的完整再现。
要计算在某个区间中不超过x的数的个数,只需要递归地进行如下操作就可以了。
*如果所给的区间和当前节点的区间完全没有交集,那么返回0个。
*如果所给的区间完全包含了当前节点对应的区间,那么使用二分搜索法对该节点上保存的数组进行查找。
*否则对两个儿子递归地进行计算之后求和即可。
由于对应同一深度的节点最多只访问常数个,因此可以在O(log2n)时间里求出不超过x的数的个数。所以整个算法的复杂度是O(nlogn + mlog3n)。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#define maxn 100000
using namespace std;
vector<int> dat[4*maxn + 50]; //线段树的数据
int a[maxn + 50];
int n, q;
//构建线段树
//k是节点的编号,和区间[l, r)对应
void build(int k, int l, int r)
{
if (r - l == 1) {
dat[k].push_back(a[l]); return;
}
int lc = k << 1, rc = k << 1 | 1;
build(lc, l, (l + r) / 2);
build(rc, (l + r) / 2, r);
dat[k].resize(r - l);
//利用STL的merge函数把两个儿子的数列合并
merge(dat[lc].begin(), dat[lc].end(), dat[rc].begin(), dat[rc].end(),dat[k].begin());
}
//计算[i, j)中不超过x的数的个数
//k是节点的编号,和区间[l, r)对应
int query(int i, int j, int x, int k, int l, int r)
{
if (j <= l || r <= i)
//完全不相交
return 0;
else if (i <= l&&r <= j){
//完全包含在里面
return upper_bound(dat[k].begin(), dat[k].end(), x) - dat[k].begin();
}
else {
//对儿子递归地计算
int lcnt = query(i, j, x, k << 1, l, (l + r) / 2);
int rcnt = query(i, j, x, k << 1 | 1, (l + r) / 2, r);
return lcnt + rcnt;
}
}
int search(int x, int y, int k)
{
int l = -1000000000 - 1;
int r = -l + 2;
while (l < r){
int mid = (l + r) >> 1;
int num = query(x, y+1, mid, 1, 1, n+1);
if (k <= num) r = mid;
else{
l = mid + 1;
}
}
return l;
}
int main()
{
while (cin >> n >> q)
{
for (int i = 1; i <= n; i++){
scanf("%d", a + i);
}
build(1, 1, n + 1);
int li, ri, ki;
for (int i = 0; i < q; i++){
scanf("%d%d%d", &li, &ri, &ki);
printf("%d\n", search(li, ri, ki));
}
}
return 0;
}
标签:
原文地址:http://blog.csdn.net/a2459956664/article/details/51302474