原文
传说中的划分树,只闻其名未见其身。然后搜索了一下划分树的资料,擦擦擦,这不就是同快排的原理+线段树的操作,两者一融合进化成了划分树么。前面两个都会,学习起来倍感轻松。
建树过程: 先对区间[1,n]内所有元素进行排序,未排序之前的数列赋值给线段树的第一层元素(tree[0][i]),然后就是同快排的原理以排序后中间元素为参 照,小于它的放在树下一层的左边,大于它的放在树下一层的右边(划分树建树以中间元素为参照,快排以第一关键元素为参照)。然后再开一个sum数 组,sum[d][i]表示第d层前i个元素有多少个元素小于as[mid](as[mid]为排序后的中间值),这样一层一层建树下去最后建完树后等于 对原数列排好了序。
查询过程: 这里最关键了。在查找区间[tl,tr]时,往下查询[tl,tr]左右孩子时,都要对区间[tl,tr]进行更新。
定义两个数s, ss, d表示第d层, k(k表示要查询的第k元素) :
s 表示区间[l,tl]有多少个元素小于as[mid], s=sum[d][tl-1];
ss 表示区间[tl,tr]有多少个元素小于as[mid], ss=sum[d][tr]-s;
if(ss>=k) 则下一层查询区间为[l+s,l+s+ss-1];
else 则下一层查询区间为[mid+1+tl-l-s,mid+1+tr-l-s-ss];
自己懒,不愿画图多解释,借用一下小媛姐姐的图。
分析一下算法复杂度: 建树nlogn+查询mlogn,很强大的说。
/****************************************************************** * HDU:4251 * 给出一列数,求区间[l,r]内第k小 ******************************************************************/ #include#include #include #include #include #include #include #include #define INF 0x3f3f3f3f #define _Clr(x, y) memset(x, y, sizeof(x)) using namespace std; typedef long long LL; const int N = 1e5+10; int arr[N], as[N]; // 原数组,排序后的数组 int sum[20][N]; // sum[d][i]表示第d层前i个数有多少个小于as[mid] int tree[20][N]; void build(int d, int l, int r) { int m = (l+r)>>1; int lp=l, rp=m+1, lm = m-l+1; for(int i=l; i<=m; i++) if(as[i]<as[m]) lm--; ///!!! 先假设前mid-l+1个数都等于as[mid],as[i]比它小则减1 for(int i=l; i<=r; i++) { if(i==l) sum[d][i] = 0; else sum[d][i] = sum[d][i-1]; if(tree[d][i]==as[m]) { if(lm) { lm--; sum[d][i]++; tree[d+1][lp++] = tree[d][i]; } else tree[d+1][rp++] = tree[d][i]; } else if(tree[d][i]< as[m]) { sum[d][i]++; tree[d+1][lp++] = tree[d][i]; } else tree[d+1][rp++] = tree[d][i]; } if(l==r) return; build(d+1, l, m); build(d+1, m+1, r); } int query_kth(int d, int l, int r, int L, int R, int k) { int s, ss, m=(l+r)>>1; if(l==r) return tree[d][l]; if(l==L) s=0, ss=sum[d][R]; // 特判 else s=sum[d][L-1], ss=sum[d][R]-s; if(ss>=k) return query_kth(d+1, l, m, l+s, l+s+ss-1, k); else return query_kth(d+1, m+1, r, m+1+L-l-s, m+1+R-l-s-ss, k-ss); } int main() { std::ios::sync_with_stdio(false); #ifndef DEBUG freopen("input", "r", stdin); #endif int n, m, a, b, lop=0; while(~scanf("%d", &n)) { for(int i=1; i<=n; i++) { scanf("%d", arr+i); tree[1][i] = as[i] = arr[i]; } sort(as+1, as+n+1); build(1, 1, n); scanf("%d", &m); printf("Case %d:\n", ++lop); while(m--) { scanf("%d%d", &a, &b); int ans = query_kth(1, 1, n, a, b, (b-a)/2+1); printf("%d\n", ans); } } return 0; }