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