【模板】划分树
多次询问一个静态区间里的第k大数。怎么搞?
暴力?(还是别想了)
多次构建树状数组?(和暴力有啥区别)
于是一个叫做划分树的东西就登场了。(据说还有个叫归并树的,速度慢一点,就不学了)
——代码
1 #include <cstdio> 2 #include <algorithm> 3 #define N 100001 4 5 using namespace std; 6 7 int n, m; 8 int c[N];//排序后数组 9 int num[20][N], val[20][N]; 10 //一共有20层,每一层元素排放,0层是原数组 11 //num[i] 表示 i 前面有多少个孩子进入左孩子 12 13 inline void build(int l, int r, int k) 14 { 15 if(l == r) return; 16 int i, mid = (l + r) / 2, lc = l, rc = mid + 1, isame = mid - l + 1; 17 //isame 表示有多少个和 c[mid] 相同的数进入左边,先假设全部进入左边 18 for(i = l; i <= r; i++) 19 if(val[k][i] < c[mid]) 20 isame--;//判断,把进入左边的且小于 c[mid] 的减去 21 for(i = l; i <= r; i++) 22 { 23 num[k][i] = i == l ? 0 : num[k][i - 1];//dp 24 if(val[k][i] < c[mid] || (val[k][i] == c[mid] && isame > 0))//进入左孩子 25 { 26 val[k + 1][lc++] = val[k][i]; 27 num[k][i]++; 28 if(val[k][i] == c[mid]) isame--; 29 } 30 else val[k + 1][rc++] = val[k][i];//到右孩子 31 } 32 build(l, mid, k + 1); 33 build(mid + 1, r, k + 1); 34 } 35 36 inline int query(int l, int r, int k, int ql, int qr, int qk) 37 { 38 if(l == r) return val[k][l];//叶子节点即为结果 39 int lnum = l == ql ? 0 : num[k][ql - 1], t = num[k][qr] - lnum, mid = (l + r) / 2;//左端点相同? 40 //lnum 表示 ql 前面有多少个数进入左孩子,t 表示 ql 和 qr 之间有多少个数进入左孩子 41 if(t >= qk) return query(l, mid, k + 1, l + lnum, l + num[k][qr] - 1, qk); 42 //全在左子树,去左子树搜索 43 else 44 { 45 //ql - l 表示 ql 前面有多少个数,再减去 lnum 表示有多少个数去了右子树 46 int kj = mid + 1 + (ql - l - lnum); 47 //ql - qr + 1 表示 ql 到 qr 有多少数,减去左边的,剩下的就是去右边的,去右边一个,下标就是kj,所以减一 48 return query(mid + 1, r, k + 1, kj, kj + qr - ql + 1 - t - 1, qk - t); 49 } 50 } 51 52 int main() 53 { 54 int i, x, y, z; 55 while(~scanf("%d %d", &n, &m)) 56 { 57 for(i = 1; i <= n; i++) scanf("%d", &val[0][i]), c[i] = val[0][i]; 58 sort(c + 1, c + n + 1); 59 build(1, n, 0); 60 for(i = 1; i <= m; i++) 61 { 62 scanf("%d %d %d", &x, &y, &z); 63 printf("%d\n", query(1, n, 0, x, y, z)); 64 } 65 } 66 return 0; 67 }
当然还有其他方法,不过代码量都比较大。
可以看看这篇blog