什么是线段树合并?

首先你需要动态开点的线段树。(对每个节点维护左儿子、右儿子、存储的数据,然后要修改某儿子所在的区间中的数据的时候再创建该节点。)

考虑这样一个问题:

你现在有两棵权值线段树(大概是用来维护一个有很多数的可重集合那种线段树,若某节点对应区间是\([l, r]\),则它存储的数据是集合中\(\ge l\)、\(\le r\)的数的个数),现在你想把它们俩合并,得到一棵新的线段树。你要怎么做呢?

提供这样一种算法(tree(x, y, z)表示一个左儿子是x、右儿子是y、数据是z的新结点):

tree *merge(int l, int r, tree *A, tree *B){
if(A == NULL) return B;
if(B == NULL) return A;
if(l == r) return new tree(NULL, NULL, A -> data + B -> data);
int mid = (l + r) >> 1;
return new tree(merge(l, mid, A -> ls, B -> ls), merge(mid + 1, r, A -> rs, B -> rs), A -> data + B -> data);
}

(上面的代码瞎写的……发现自己不会LaTeX写伪代码,于是瞎写了个“不伪的代码”,没编译过,凑付看 ><)

这个算法的复杂度是多少呢?显然是A、B两棵树重合的节点的个数。

那么假如你手里有m个只有一个元素的“权值线段树”,权值范围是\([1, n]\),想都合并起来,复杂度是多少呢?复杂度是\(O(m\log n)\)咯。

这个合并线段树的技巧可以解决一些问题——例如这个:BZOJ 2212

题意:

给出一棵完全二叉树,每个叶子节点有一个权值,你可以任意交换任意节点的左右儿子,然后DFS整棵树得到一个叶子节点组成的序列,问这个序列的逆序对最少是多少。

可以看出,一个子树之内调换左右儿子,对子树之外的节点没有影响。于是可以DFS整棵树,对于一个节点的左右儿子,如果交换后左右儿子各出一个组成的逆序对更少则交换,否则不交换。如何同时求出交换与不交换左右儿子情况下的逆序对数量?可以使用线段树合并。

用两个权值线段树分别表示左右儿子中所有的数的集合。在合并两棵线段树的同时,A -> right_sonB -> left_son可以构成不交换左右儿子时的一些逆序对,A -> left_sonB -> right_son可以构成交换左右儿子时的一些逆序对,其余的逆序对在线段树AB的左右子树中,可以在递归合并的时候处理掉。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;
typedef long long ll;
template <class T>
void read(T &x){
char c;
bool op = 0;
while(c = getchar(), c < '0' || c > '9')
if(c == '-') op = 1;
x = c - '0';
while(c = getchar(), c >= '0' && c <= '9')
x = x * 10 + c - '0';
if(op) x = -x;
}
template <class T>
void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
} const int N = 10000005;
int n, tmp, ls[N], rs[N], data[N], tot;
ll ans, res1, res2; int newtree(int l, int r, int x){
data[++tot] = 1;
if(l == r) return tot;
int mid = (l + r) >> 1, node = tot;
if(x <= mid) ls[node] = newtree(l, mid, x);
else rs[node] = newtree(mid + 1, r, x);
return node;
}
int merge(int l, int r, int u, int v){
if(!u || !v) return u + v;
if(l == r) return data[++tot] = data[u] + data[v], tot;
int mid = (l + r) >> 1, node = ++tot;
res1 += (ll)data[rs[u]] * data[ls[v]], res2 += (ll)data[ls[u]] * data[rs[v]];
ls[node] = merge(l, mid, ls[u], ls[v]);
rs[node] = merge(mid + 1, r, rs[u], rs[v]);
data[node] = data[ls[node]] + data[rs[node]];
return node;
}
int dfs(){
read(tmp);
if(tmp) return newtree(1, n, tmp);
int node = merge(1, n, dfs(), dfs());
ans += min(res1, res2);
res1 = res2 = 0;
return node;
} int main(){
read(n);
dfs();
write(ans), enter;
return 0;
}

神奇的操作——线段树合并(例题: BZOJ2212)的更多相关文章

  1. BZOJ2212 POI2011Tree Rotations(线段树合并)

    显然子树内的操作不会对子树外产生影响.于是贪心,若交换之后子树内逆序对减少就交换. 这个东西可以用权值线段树计算.操作完毕后需要对两棵权值线段树合并,这个的复杂度是两棵线段树的重复节点个数.那么总复杂 ...

  2. BZOJ2212 [Poi2011]Tree Rotations 线段树合并 逆序对

    原文链接http://www.cnblogs.com/zhouzhendong/p/8079786.html 题目传送门 - BZOJ2212 题意概括 给一棵n(1≤n≤200000个叶子的二叉树, ...

  3. 2018.07.07 BZOJ2212: Poi2011Tree Rotations(线段树合并)

    2212: [Poi2011]Tree Rotations Time Limit: 20 Sec Memory Limit: 259 MB Description Byteasar the garde ...

  4. BZOJ2212 [Poi2011]Tree Rotations 【线段树合并】

    题目链接 BZOJ2212 题解 一棵子树内的顺序不影响其与其它子树合并时的答案,这一点与归并排序的思想非常相似 所以我们只需单独处理每个节点的两棵子树所产生的最少逆序对即可 只有两种情况,要么正序要 ...

  5. 【bzoj2333 & luoguP3273】棘手的操作(线段树合并)

    题目传送门:bzoj2333 luoguP3273 这操作还真“棘手”..听说这题是可并堆题?然而我不会可并堆.于是我就写了线段数合并,然后调了一晚上,数据结构毁一生!!!QAQ…… 其实这题也可以把 ...

  6. 【BZOJ2212】[Poi2011]Tree Rotations 线段树合并

    [BZOJ2212][Poi2011]Tree Rotations Description Byteasar the gardener is growing a rare tree called Ro ...

  7. bzoj2212[Poi2011]Tree Rotations [线段树合并]

    题面 bzoj ans = 两子树ans + min(左子在前逆序对数, 右子在前逆序对数) 线段树合并 #include <cstdio> #include <cstdlib> ...

  8. 【BZOJ2212】[POI2011]Tree Rotations (线段树合并)

    题解: 傻逼题 启发式合并线段树里面查$nlog^2$ 线段树合并顺便维护一下$nlogn$ 注意是叶子为n 总结点2n 代码: #include <bits/stdc++.h> usin ...

  9. [bzoj2212]Tree Rotations(线段树合并)

    解题关键:线段树合并模板题.线段树合并的题目一般都是权值线段树,因为结构相同,求逆序对时,遍历权值线段树的过程就是遍历所有mid的过程,所有能求出所有逆序对. #include<iostream ...

随机推荐

  1. R绘图 第十二篇:散点图(高级)

    散点图用于描述两个连续性变量间的关系,三个变量之间的关系可以通过3D图形或气泡来展示,多个变量之间的两两关系可以通过散点图矩阵来展示. 一,添加了最佳拟合曲线的散点图 使用基础函数plot(x,y)来 ...

  2. 开启mac上印象笔记的代码块

    Mac 印象笔记左上角菜单栏:偏好设置-->软件更新-->开启代码块 (Preferences -> Software Update -> Enable code block) ...

  3. BGFX 渲染引擎中着色器代码的调试方法

    在实时渲染的图形开发中,着色器代码(Shader)越来越复杂,于是单纯的靠经验和不断试错的开发和调试方法早已不能满足实际需求.使用调试工具进行调试,成为开发中重要的方法.Bgfx 是一款跨平台.抽象封 ...

  4. SpringBoot笔记--Jackson

    SpringUtil.getBean<GenericConversionService>().addConverter(Date2LocalDateTimeConverter()) var ...

  5. mysql主从同步(2)-问题梳理

    之前详细介绍了Mysql主从复制的原理和部署过程,在mysql同步过程中会出现很多问题,导致数据同步异常.以下梳理了几种主从同步中可能存在的问题:1)slave运行过慢不能与master同步,也就是M ...

  6. SQL中not in 和not exists

    在SQL中倒是经常会用到子查询,而说到子查询,一般用的是in而不是exists,先不谈效率问题,就先说说会遇到哪些问题. 用到in当取反的时候,肯定先想到的就是not in.但是在使用not in的时 ...

  7. bootstrap面试题

    1.你能描述一下渐进增强和优雅降级之间的不同吗? 优雅降级:Web站点在所有新式浏览器中都能正常工作,如果用户使用的是老式浏览器,则代码会检查以确认它们是否能正常工作.由于IE独特的盒模型布局问题,针 ...

  8. 同步手绘板——json

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.它基于ECMAScript的一个子集. JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族 ...

  9. EL表达式和JSTL标签库

    expresion language表达式语言 可以输出表达式的值.跟jsp的表达式脚本一样.计算表达式的值后输出.  EL表达式出现的目的是为了使JSP写起来更加简单,让jsp的代码更佳简化. 1. ...

  10. ajax跨域请求数据

    最近开始接触ajax的跨域请求问题,相比网上说的一大堆,我这里就说得比较浅显了. 关于为什么要跨域这个问题,实际的需求是当网站项目部署在一个域名上的时候,分域可以很好地解决网站卡顿问题(拥有多台服务器 ...