题目:给出一个数列,对于每个查询(i,j,k),输出ai.....aj中排序后第k个数。
分析:
其实就是线段树的运用。
二分答案,然后对于i...j计算小于k的数字个数,这个用线段树模拟归并排序,然后如果当前区间被i....j包含,那么就返回这个区间的二分后的结果,否则查找子区间。
跟AC的程序对拍十几个极端数据,可是死都不过,不懂。线段树就是我的克星。
参考程序:
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
const int maxn=110000;
vector<int> c[4*maxn];
int a[maxn],b[maxn];
int n;
void build(int p,int l,int r){
if (l+1==r)c[p].push_back(a[l]);
else{
int mid=(l+r)>>1;
int lch=2*p,rch=2*p+1;
build(lch,l,mid);
build(rch,mid,r);
c[p].resize(r-l);
merge(c[lch].begin(),c[lch].end(),c[rch].begin(),c[rch].end(),c[p].begin());
}
}
int query(int p,int l,int r,int ql,int qr,int x){
if (qr<=l || r<=ql)return 0;else
if (ql<=l && r<=qr){
return upper_bound(c[p].begin(),c[p].end(),x)-c[p].begin();
}else{
int mid=(l+r)>>1,t=0;
if (ql<mid)t+=query(2*p,l,mid,ql,qr,x);
if (mid<qr)t+=query(2*p+1,mid,r,ql,qr,x);
return t;
}
}
int main(){
int T;
while (scanf("%d%d",&n,&T)==2){
for (int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+n+1);
build(1,1,n+1);
while (T--){
int ql,qr,K;
scanf("%d%d%d",&ql,&qr,&K);
int l=-1,r=n+1;
while (l<r){
int mid=(l+r)>>1;
if (query(1,1,n+1,ql,qr+1,b[mid])>=K)r=mid;
else l=mid+1;
}
printf("%d\n",b[l]);
}
}
return 0;
}
正确的:
#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()
{ freopen("make.out","r",stdin);
freopen("out.out","w",stdout);
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;
}