首页 > 代码库 > 【模板】划分树

【模板】划分树

K-th Number

多次询问一个静态区间里的第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 }
View Code

当然还有其他方法,不过代码量都比较大。

可以看看这篇blog

【模板】划分树