线段树
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树相关的知识,希望对你有一定的参考价值。
数组
题意
给定一个长度为 n 的颜色数组 col[] .
两种操作:
修改某个点的颜色.
查询不含有重复颜色的区间个数.
分析
设 pr[i] 表示与 i 相同的上一个颜色, $y_r$ 表示以 r 为右端点的最小左端点, 那么答案为 $\\sum_{r} \\max_{i \\le r} pr_i = \\sum_{r} y_r$ .
用 set 维护 pr[] , 用线段树维护 y[] .
实现
线段树需要存储 sum, Max , sum 为只考虑区间内部的元素的 y 的和, Max 表示区间内最大的 pr , 难点在于合并.
注意到合并之后, y 值会出现这样的情况:
记录 sum 为没有变化的和, size 为取得 LMax 的个数, 在二分找到最后的变化位置时进行维护.
1. 若 LMax > 区间的 Max , 那么 size 加上区间大小.
2. 若是叶子节点, 则说明当前节点是取原来的第一个, sum 加上 pr[当前点] .
3. 递归访问左边, 若有断点, 则 sum 加上 sum[x] - sum[Lc] , 注意不能用 sum[Rc] , 因为没有考虑到左边的贡献; 若没有端点, 那么递归访问右边.
1 inline bool Find(int x, int Max, LL &sum, LL &siz) { 2 if (Max > tr[x].Max) return siz += tr[x].r-tr[x].l+1, true; 3 if (tr[x].l == tr[x].r) return sum += tr[x].sum, false; 4 bool tag = Find(Lc, Max, sum, siz); 5 tag ? Find(Rc, Max, sum, siz) : sum += tr[x].sum-tr[Lc].sum; 6 return false; 7 } 8 inline void Up(int x) { 9 LL sum = 0, siz = 0; 10 Find(Rc, tr[Lc].Max, sum, siz); 11 tr[x].Max = max(tr[Lc].Max, tr[Rc].Max); 12 tr[x].sum = tr[Lc].sum + siz * tr[Lc].Max + sum; 13 }
[HDU 5930] Different GCD Subarray Query II
题意
给定序列 $A$ , $m$ 次修改某个点的点权, 并询问当前的所有区间的不同 GCD 个数.
分析
单组询问怎么做? 除了维护 L[], R[] 的做法之外, 我们还可以考虑分治, 统计经过 mid = (l+r) / 2 的个数. 我们求出从 mid 往左边拓展的 GCD 列表, 以及 mid 往右边拓展的 GCD 列表, 然后枚举每一对, 并启动累加器.
多组询问? 考虑动态维护分治结构, 即用线段树的每个节点维护 mid 往左右拓展的 List , 记录 cnt[x] 表示数值 x 被线段树的几组点对统计到, GCD 为 x 的区间存在, 当且进当 cnt[x] > 0 .
小结
线段树的一种本质是动态维护分治结构.
以上是关于线段树的主要内容,如果未能解决你的问题,请参考以下文章