「NOI.AC」Leaves 线段树合并
题目描述
现在有一棵二叉树,所有非叶子节点都有两个孩子。在每个叶子节点上有一个权值(有个叶子节点,满足这些权值为的一个排列)。可以任意交换每个非叶子节点的左右孩子。 要求进行一系列交换,使得最终所有叶子节点的权值按照遍历序写出来,逆序对个数最少。
输入
第一行表示叶子结点个数
接下来每行一个数。如果为,表示这个节点为非叶子节点,递归地向下读入其左孩子和右孩子的信息。如果不为,表示这个节点是叶子节点,权值为。
输出
输出一行,表示最少逆序对个数。
样例输入
3
0
0
3
1
2
样例输出
1
数据范围
对于前的数据,
对于前的数据,
对于的数据,
题解
线段树合并对我来说还是一个新的算法呢。
首先进行简单分析,发现某一个非叶子节点是否交换无法影响到父亲及以上的节点产生的逆序对,只需要计算出每一个非叶子节点子树内交换与否产生的最小逆序对数量,全部加起来即可(独善其身)。
然后考虑如何计算左右子树哪个产生的逆序对数量,并应考虑到合并左右子树的信息的实现方式。由此可以联想到线段树合并——这样可维护子树的信息并快速合并到父亲。然后考虑如何算答案。我们可以在合并的时候进行计算。对于一个值,可以轻而易举地算出来合并中的两子树小于等于或大于它的数量(即)。左子树大于的和右子树小于等于的乘积即为这一层中不交换的逆序对数量,反之亦然。我们合并时肯定会合并到最后一层,路上就可以计算出交换与否的逆序对数量,两者取最小值加到答案中即可。
注意,尤其是中计算某一个子树的逆序对时。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define N 400005
#define lim 20
#define ll long long
void Read(int &p)
{
p = 0;
int f = 0;
char c = getchar();
for (; c < '0' || c > '9'; c = getchar())f = (c == '-');
for (; c >= '0' && c <= '9'; c = getchar())p = p * 10 + c - '0';
if (f)p = -p;
}
int n, tot, root[N];
int rt, lc[N], rc[N];
int sz[N], val[N], cnt;
ll ans, a, b;
struct node
{
int lc, rc, sz;
}S[N * lim];
void build(int &q, int l, int r, int v)
{
if (!q)
q = ++tot;
S[q].sz++;
if (l == r)
return;
int mid = (l + r) >> 1;
if (v <= mid)
build(S[q].lc, l, mid, v);
else
build(S[q].rc, mid + 1, r, v);
}
int Merge(int A, int B)
{
if (!A)return B;
if (!B)return A;
a += 1ll * S[S[A].lc].sz * S[S[B].rc].sz;
b += 1ll * S[S[A].rc].sz * S[S[B].lc].sz;
S[A].lc = Merge(S[A].lc, S[B].lc);
S[A].rc = Merge(S[A].rc, S[B].rc);
S[A].sz = S[A].sz + S[B].sz;
return A;
}
void Get(int &q)
{
q = ++cnt;
Read(val[q]);
if (!val[q])
{
Get(lc[q]), Get(rc[q]);
a = b = 0;
root[q] = Merge(root[lc[q]], root[rc[q]]);
ans += min(a, b);
}
else
build(root[q], 1, n, val[q]);
}
int main()
{
Read(n);
Get(rt);
printf("%lld\n", ans);
}
作者:ModestStarlight
出处:http://www.cnblogs.com/ModestStarlight/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
分类:
数据结构 - 线段树合并
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现