更新地址:传送门

---

权值线段树

所谓权值线段树,就是一种维护而非下标的线段树,我个人倾向于称呼它为值域线段树。

举个栗子:对于一个给定的数组,普通线段树可以维护某个子数组中数的和,而权值线段树可以维护某个区间内数组元素出现的次数。

在实现上,由于值域范围通常较大,权值线段树会采用离散化或动态开点的策略优化空间。

更新操作:

更新的时候,我们向线段树中插入一个值v,那么所有包含v的区间值都需要+1。(每个节点维护对应区间中出现了多少个数)

int update (long long v,long long l,long long r,int pos) { // 插入v,当前区间为[l,r]。
if (!pos) pos=++tot_node;
// 如果该节点不存在,则新建节点。
if (l<=v&&v<=r) {
// 如果当前区间包含插入值。
tree[pos].val++;
// 出现次数+1。
if (l==r) return pos;
// 如果递归到叶子节点,退出。
}
long long mid=(l+r)>>;
if (v<=mid) tree[pos].ls=update(v,l,mid,tree[pos].ls);
else tree[pos].rs=update(v,mid+,r,tree[pos].rs);
// 判断插入值是在当前区间的哪一半。
pushup(pos);
// 回溯。
return pos;
}

查询操作:

查询操作类似二叉树。

 1 long long query (long long l,long long r,long long L,long long R,int pos) { // 查询区间[L,R]中数字的数量,当前区间为[l,r]。
2 if (!pos) return 0;
3 // 如果该节点不存在,必然没有到达过。
4 if (L<=l&&r<=R) {
5 // 如果当前区间属于查询区间。
6 return tree[pos].val;
7 // 直接返回区间中数字的数量。
8 }
9 long long mid=(l+r)>>1,ans=0;
10 if (L<=mid) ans+=query(l,mid,L,R,tree[pos].ls);
11 if (mid<R) ans+=query(mid+1,r,L,R,tree[pos].rs);
12 // 统计区间和。
13 return ans;
14 }

练习题:

作为练习模板,可以考虑逆序对。大体思路是每次查询a[i]+1~n的元素个数。


线段树合并

所谓线段树合并,就是通过将合并两颗线段树获得信息,其正确性由线段树的稳定结构保证。

线段树合并通常是一个自底向上的过程,在深搜的途中将子节点的树合并到父节点上,从而实现对父节点值的统计。

线段树合并的复杂度是$nlogn$,比启发式合并少一个$logn$。

不难发现,如果按照线段树合并的原始思想直接在每一个需要遍历的节点上都单独建立一个线段树肯定会爆空间。在这里可以使用被称为“回收内存”的方法:由于在合并之后子节点的信息已经归入父节点,所以子节点没有用处,那么可以将其所有节点回收丢入一个内存池,往后更新的时候可以从内存池里取节点而非新建节点。

回收内存:

 inline int newId () {
if (pool_top) return mempool[pool_top--];
return ++tot_node;
}
inline void killId (int &x) {
mempool[++pool_top]=x;
tree[x].ls=tree[x].rs=tree[x].val=;
x=;
}

合并操作:

合并操作与左偏树的合并有一点像,就是递归合并每一个节点。

 int merge (int l,int r,int x,int y) {
if (!x||!y) return x+y;
int now=newId(),mid=(l+r)>>;
if (l==r) {
tree[now].val=tree[x].val+tree[y].val;
} else {
tree[now].ls=merge(l,mid,tree[x].ls,tree[y].ls);
tree[now].rs=merge(mid+,r,tree[x].rs,tree[y].rs);
tree[now].val=tree[tree[now].ls].val+tree[tree[now].rs].val;
}
killId(x),killId(y);
return now;
}

练习题:

可以考虑做一下Tree Rotation,大致题意是给一棵二叉树,可以交换每个点的左右子树,要求前序遍历叶子的逆序对最少。

由于左右儿子的交换不会影响更上层的值,所以在每次合并的时候直接统计即可。

权值线段树&线段树合并的更多相关文章

  1. HihoCoder1576 子树中的最小权值( dfs序 +线段树 || 树剖)

    给定一棵N个节点的树,编号1~N.其中1号节点是根,并且第i个节点的权值是Vi. 针对这棵树,小Hi会询问小Ho一系列问题.每次小Hi会指定一个节点x,询问小Ho以x为根的子树中,最小的权值是多少.为 ...

  2. 【BZOJ3065】带插入区间K小值 替罪羊树+权值线段树

    [BZOJ3065]带插入区间K小值 Description 从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i].跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴.这时跳蚤国王决定理 ...

  3. BZOJ4771七彩树——可持久化线段树+set+树链的并+LCA

    给定一棵n个点的有根树,编号依次为1到n,其中1号点是根节点.每个节点都被染上了某一种颜色,其中第i个节 点的颜色为c[i].如果c[i]=c[j],那么我们认为点i和点j拥有相同的颜色.定义dept ...

  4. BZOJ3110 K大数查询 【线段树 + 整体二分 或 树套树(非正解)】

    Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个位置到第b个位 ...

  5. 97: cf 983E 倍增+树套树

    $des$一棵 $n$ 个点的树,树上有 $m$ 条双向的公交线路,每条公交线路都在两个节点之间沿最短路径往返.$q$ 次询问从一个点要到达另一个点,在只坐公交的情况下,至少需要坐几辆公交车:或者判断 ...

  6. B20J_2733_[HNOI2012]永无乡_权值线段树合并

    B20J_2733_[HNOI2012]永无乡_权值线段树合并 Description:n座岛,编号从1到n,每座岛都有自己的独一无二的重要度,按照重要度可以将这n座岛排名,名次用1到 n来表示.某些 ...

  7. 【bzoj1977】[BeiJing2010组队]次小生成树 Tree 最小生成树+权值线段树合并

    题目描述 求一张图的严格次小生成树的边权和,保证存在. 输入 第一行包含两个整数N 和M,表示无向图的点数与边数. 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z ...

  8. [bzoj 2733]启发式合并权值线段树

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2733 平衡树待学习.从一个博客学到了合并权值线段树的姿势:http://blog.csdn ...

  9. 【bzoj4719】[Noip2016]天天爱跑步 权值线段树合并

    题目描述 给出一棵n个点的树,以及m次操作,每次操作从起点向终点以每秒一条边的速度移动(初始时刻为0),最后对于每个点询问有多少次操作在经过该点的时刻为某值. 输入 第一行有两个整数N和M .其中N代 ...

  10. 【bzoj2212】[Poi2011]Tree Rotations 权值线段树合并

    原文地址:http://www.cnblogs.com/GXZlegend/p/6826614.html 题目描述 Byteasar the gardener is growing a rare tr ...

随机推荐

  1. [java设计模式]之单例模式

    -------------------此部分比較深入地解说了单例模式,原文链接已给出.兴许将涉及一些常见面试问题--------------------------- 原文地址:http://www. ...

  2. sublime text3编译C/C++系统提示丢失zlib1.dll解决的方法

    用g++ 编译C/C++程序时 出现例如以下系统提示: 解决的方法例如以下: 1.下载zlib1.dll: http://www.pc6.com/softview/SoftView_81060.htm ...

  3. ASP.NET MVC中的嵌套布局页

    在WEB窗体模式中,用惯了母版页,并且常有母版页嵌套的情况. 而在MVC模式下,对应母版页的,称作为布局页.默认的布局页为 ~/Views/Shared/_Layout.cshtml.默认每个页面都会 ...

  4. 如何运行开源的React Native项目?

    如何运行开源的RN项目? 1.下载 2.解压 3.配置本地sdk位置 sdk.dir = D\:\\Android\\SDK 4.调整gradle版本 apply plugin: "com. ...

  5. bzoj4977 跳伞求生——贪心

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4977 今天讲的贪心题,真神奇啊: 首先,要得到尽量多选队友的解: 把队友按 a[i] 从小到 ...

  6. E20170826-hm

    squash   vt. 挤进; 将(某人[某物])压扁; 使沉默; 平定(叛乱等); meld vt. (使) 融合,合并,结合; n. 混合,合并; amend  vt. 修订; 改良,修改; a ...

  7. 【钓起来的tips系列】

    一.求n的阶乘: #include<bits/stdc++.h> using namespace std; int n; int jc(int k) { ); )*k; } /*int j ...

  8. Java并发基础知识点详解

    1.synchronized与Lock区别 父类有synchtonized,子类调用父类的同步方法,是没办法同步的,因为synchronized不是修饰符,不会被继承下来. synchronized ...

  9. JavaScript学习二

    2019-05-30 15:08:24 加油,这几天在赶高数,都…… <!DOCTYPE html> <html> <head> <script type=& ...

  10. B - Substrings Sort

    Problem description You are given nn strings. Each string consists of lowercase English letters. Rea ...