http://poj.org/problem?id=2104
题意:求任意区间第k大数
分析:划分树主要参考了大牛博客
http://www.notonlysuccess.com/?p=142
http://blog.sina.com.cn/s/blog_5f5353cc0100ki2e.html
划分树兼容了快排和归并的特点。。。采用划分的思想逐渐往下划,最后叶子节点的元素就是排好序的元素。。。其实可以理解成稳定的快排。。。和归并树一样,记录每一层的结果。。。并记录toleft[]。。。
查找的过程,总是查询[ tt[p].l [ l, r] tt[p].r ]。。。这里就有了几个区间,想想,划分树划分下去的时候[tt[p].l, l-1]中不大于中位数的一定靠在最前,[l, r]中不大于中位数的排在其后。。。我们通过计算[l, r]中不大于中位数的个数l2来和k比较,来确定是到左子树还是右子树去找,查找的范围都可以通过两个区间划分到左子树的个数来缩小。。。。
时间复杂度nlgn+mlgn,比归并树nlgn+m(lgn)^3快多了,m越大效果越好。。
代码:
比归并树快一倍吧。。。
#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <iostream>
using namespace std;
const int N=100010;
const int DEEP=20;
int n, m, sa[N];
int seq[DEEP][N]; //归并树
int toleft[DEEP][N]; //记录当前层左边界到i位置有多少划入左子树
struct node
{
int l, r, mid;
} tt[N*4];
void build(int l, int r, int d, int p)
{
tt[p].l = l;
tt[p].r = r;
tt[p].mid = (l+r)>>1;
if(l==r)
return;
int lsame, i, j, ii;
lsame = tt[p].mid-tt[p].l+1;
for(i=tt[p].l; i<=tt[p].r; i++)
if(seq[d][i]<sa[tt[p].mid])
lsame--;
i = tt[p].l;
j = tt[p].mid+1;
for(ii=tt[p].l; ii<=tt[p].r; ii++)
{
if(ii==tt[p].l)
toleft[d][ii] = 0;
else
toleft[d][ii] = toleft[d][ii-1];
if(seq[d][ii]<sa[tt[p].mid])
{
toleft[d][ii]++;
seq[d+1][i++] = seq[d][ii];
}
else if(seq[d][ii]>sa[tt[p].mid])
{
seq[d+1][j++] = seq[d][ii];
}
else
{
if(lsame>0)
{
toleft[d][ii]++;
lsame--;
seq[d+1][i++] = seq[d][ii];
}
else
{
seq[d+1][j++] = seq[d][ii];
}
}
}
build(l, tt[p].mid, d+1, p*2);
build(tt[p].mid+1, r, d+1, p*2+1);
}
int query(int l, int r, int k, int d, int p)
{
if(l==r)
return seq[d][l];
//l1为[tt[p].l, l-1]之间划进左子树的个数,l2为[l, r]之间划进左子树的个数。。
int l1, l2;
if(l==tt[p].l)
{
l1 = 0;
l2 = toleft[d][r];
}
else
{
l1 = toleft[d][l-1];
l2 = toleft[d][r]-toleft[d][l-1];
}
int ll, rr;
if(k<=l2)
{
ll = tt[p].l + l1;
rr = tt[p].l + l1+l2 -1;
return query(ll, rr, k, d+1, p*2);
}
else
{
//r1表示[tt[p].l, l]之间划进右子树的个数,r2为[l, r]之间划进左子树的个数。。
int r1, r2;
r1 = l-tt[p].l-l1;
r2 = r-l+1-l2;
ll = tt[p].mid+1 + r1;
rr = tt[p].mid+1 + r1+r2 -1;
return query(ll, rr, k-l2, d+1, p*2+1);
}
}
int main()
{
int i, j, x, y, k;
while(scanf("%d%d", &n, &m)!=EOF)
{
for(i=0; i<n; i++)
{
scanf("%d", &seq[0][i]);
sa[i] = seq[0][i];
}
sort(sa, sa+n);
build(0, n-1, 0, 1);
while(m--)
{
scanf("%d%d%d", &x, &y, &k);
printf("%d\n", query(x-1, y-1, k, 0, 1));
}
}
return 0;
}