线段树

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 .

  小结

  线段树的一种本质是动态维护分治结构.

以上是关于线段树的主要内容,如果未能解决你的问题,请参考以下文章

线段树

CCF(除法):线段树区间修改(50分)+线段树点修改(100分)+线段树(100分)

线段树合并

数据结构——线段树

论线段树:二

线段树