权值线段树
线段树版的桶
桶可以记录数据的分布。若要动态查询数据中,给定区间内共有多少个数,就可以将桶用线段树进行处理,这种线段树叫做权值线段树。
- 记录权值的线段树。记录权值指的是,每个点上存的是区间内数字出现的总次数。比如一个长度为 \(10\) 的数组
[1,1,2,3,3,4,4,4,4,5]
- 其中数字 \(1\) 出现了两次,那么 \([1,1]\) 这个区间的值为 \(2\),数字 \(2\) 出现了一次,那么 \([2,2]\) 这个区间的值为 \(1\)
- 通过
push_up
可以得到,\([1,2]\) 这个节点的值为 \(3\),即 \(1\) 出现的次数和 \(2\) 出现的次数加和。 - 如果我想要知道这个数组上的第 \(k\) 小,我就可以在这个权值线段树上用 \(O(\log n)\)的时间来实现。
- 如果原始输入的值域范围比较大,可能需要先离散化。
例题
P1908 逆序对
代码
权值线段树的动态开点
使用上面的普通线段树改造的权值线段树存在各种问题:
- 有时,线段树需要维护的值域很大,但是实际用到的节点很少;
- 强制在线不能离散化;
于是,我们可以写出动态开点的权值线段树:
- 初始时线段树中没有节点,用到的时候再向内存要。每一次单点修改都会产生一条从根走向叶子的路径,总空间消耗 \(O(n\log V)\)(\(n\) 表示调用
insert()
的次数); - 查询时若遇到空节点,则说明该区间内没有信息,返回 \(0\) 或 \(\pm \infty\) 即可。
例题
U74894 有便便的厕所
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
|
U74895 有便便的厕所2
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
|