#include <iostream> #include <algorithm> #include <cstring> #include <string> #include <cstdio> #include <cmath> #include <queue> #include <map> #include <set> #define eps 1e-5 #define MAXN 111111 #define MAXM 222222 #define INF 1000000001 #define lch(x) x<<1 #define rch(x) x<<1|1 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 using namespace std; int a[20][MAXN], t[MAXN], n, q; void build(int l, int r, int deep) { if(l == r) { a[deep][l] = t[l]; return; } int m = (l + r) >> 1; build(l, m, deep + 1); build(m + 1, r, deep + 1); int i = l, j = m + 1, k = l; while(i <= m && j <= r) { if(a[deep + 1][i] < a[deep + 1][j]) a[deep][k++] = a[deep + 1][i++]; else a[deep][k++] = a[deep + 1][j++]; } while(i <= m) a[deep][k++] = a[deep + 1][i++]; while(j <= r) a[deep][k++] = a[deep + 1][j++]; } int find(int low, int high, int deep, int v) { if(a[deep][low] > v) return 0; if(a[deep][high] < v) return high - low + 1; int l = low, r = high; while(l <= r) { int m = (l + r) >> 1; if(a[deep][m] >= v) r = m - 1; else l = m + 1; } return l - low; } int query(int L, int R, int l, int r, int deep, int p) { if(L <= l && R >= r) return find(l, r, deep, p); int m = (l + r) >> 1; int ret = 0; if(L <= m) ret += query(L, R, l, m, deep + 1, p); if(R > m) ret += query(L, R, m + 1, r, deep + 1, p); return ret; } int main() { while(scanf("%d%d", &n, &q) != EOF) { for(int i = 1; i <= n; i++) scanf("%d", &t[i]); build(1, n, 1); int x, y, k; while(q--) { scanf("%d%d%d", &x, &y, &k); int l = 1, r = n; while(l <= r) { int m = (l + r) >> 1; int pos = query(x, y, 1, n, 1, a[1][m]) + 1; if(pos <= k) l = m + 1; else r = m - 1; } printf("%d\n", a[1][r]); } } return 0; }
然后就是划分树了
感觉上划分树就是归并树逆着来
它是对每个区间,分为两个孩子,左孩子存小的那一半数,右孩子存大的那一半数,孩子中数的顺序同父区间的相对顺序是一样的。
然后每层,每个数都要记录一个信息,记录其左边有多少个数进入了左孩子区间,这是为了查询时有用
查询的话,假设我们查询的区间为L,R
首先进入的是1~n区间,我们要计算L,R区间中有多少个进入了左孩子区间,假设为x,这时我们刚才记录的信息就用的上了
若x>=k ,那么我们就要进入左孩子区间进行查询,而查询的区间要进行调整
若x<k,那么我们就进入右孩子区间,然后查询的区间要调整
想看详细的话去小hh大牛blog看 http://www.notonlysuccess.com/index.php/divide-tree/
#include <iostream> #include <algorithm> #include <cstring> #include <string> #include <cstdio> #include <cmath> #include <queue> #include <map> #include <set> #define eps 1e-5 #define MAXN 111111 #define MAXM 1111111 #define INF 1000000008 using namespace std; int a[MAXN], p[MAXN], rank[MAXN], lnum[20][MAXN], t[MAXN]; int n, q; bool cmp(int x, int y) { if(a[x] != a[y]) return a[x] < a[y]; else return x < y; } void build(int l, int r, int deep) { if(l == r) return; int m = (l + r) >> 1; int cnt = 0, j = l, k = m + 1; for(int i = l; i <= r; i++) { if(rank[i] <= m) t[j++] = rank[i], lnum[deep][i] = ++cnt; else t[k++] = rank[i], lnum[deep][i] = cnt; } for(int i = l; i <= r; i++) rank[i] = t[i]; build(l, m, deep + 1); build(m + 1, r, deep + 1); } int query(int L, int R, int l, int r, int k, int deep) { if(l == r) return a[p[l]]; int m = (l + r) >> 1; int tl = 0; if(l < L) tl = lnum[deep][L - 1]; int tn = lnum[deep][R] - tl; if(tn >= k) return query(l + tl, l + tl + tn - 1, l, m, k, deep + 1); else { int pos = m + L - l - tl + 1;// L-l-tl表示从l到L-1有多少个分到了右孩子 return query(pos, pos + R - L - tn, m + 1 ,r , k - tn, deep + 1); } } int main() { while(scanf("%d%d", &n, &q) != EOF) { for(int i = 1; i <= n; i++) scanf("%d", &a[i]), p[i] = i; sort(p + 1, p + n + 1, cmp); for(int i = 1; i <= n; i++) rank[p[i]] = i; build(1, n, 1); int x, y, z; while(q--) { scanf("%d%d%d", &x, &y, &z); printf("%d\n", query(x, y, 1, n, z, 1)); } } return 0; }