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; }