划分树的基本功能是,对一个给定的数组,求区间[l,r]内的第k大(小)数。
划分树的基本思想是分治,每次查询复杂度为O(log(n)),n是数组规模。
具体原理见http://baike.baidu.com/link?url=vIUKtsKYx7byeS2KCOHUI14bt_0sdHAa9BA1VceHdGsTv5jVq36SfZgBKdaHYUGqIGvIGrE_aJtqy0D0b1fCoq
个人感觉看代码是最好的学习方法。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define N 100100 int a[N]; int s[30][N];//划分树 int num[30][N];//num[i][j] - num[i][j-1] == 1表示第i层第j个数在下层中要被划入左子树 void build(int l, int r, int dep = 1) { if(l == r){ s[dep][l] = s[dep-1][l]; return; } int mid = l+r>>1; int cnt = mid-l+1; for(int i = l; i <= r; i++) if(s[dep-1][i] < a[mid]) cnt--; int c1 = l, c2 = mid+1; for(int i = l; i <= r; i++) { if(s[dep-1][i] < a[mid] || ( s[dep-1][i] == a[mid] && cnt-- > 0)) s[dep][c1++] = s[dep-1][i]; else s[dep][c2++] = s[dep-1][i]; num[dep-1][i] = num[dep-1][l-1]+c1-l; } build(l, mid, dep+1); build(mid+1, r, dep+1); } int query(int l, int r, int k, int L, int R, int dep = 0) { if(l == r) return s[dep][l]; int mid = L+R>>1; int cnt = num[dep][r] - num[dep][l-1]; if(cnt >= k) { int nl = L+num[dep][l-1]-num[dep][L-1]; int nr = nl+cnt-1; return query(nl, nr, k, L, mid, dep+1); } else { int nr = r+num[dep][R]-num[dep][r]; int nl = nr - (r-l-cnt); return query(nl, nr, k-cnt, mid+1, R, dep+1); } } int main() { int T, n, m; scanf("%d", &T); while(T--) //while(~scanf("%d %d", &n, &m)) { memset(num, 0, sizeof(num)); scanf("%d %d", &n, &m); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); for(int i = 1; i <= n; i++) s[0][i] = a[i]; sort(a+1, a+n+1); build(1, n); for(int i = 0; i < m; i++) { int l, r, k; scanf("%d %d %d", &l, &r, &k); printf("%d\n", query(l, r, k, 1, n)); } } return 0; }
时间: 2024-10-05 11:34:50