题目描述

现在有一棵二叉树,所有非叶子节点都有两个孩子。在每个叶子节点上有一个权值(有\(n\)个叶子节点,满足这些权值为\(1\dots n\)的一个排列)。可以任意交换每个非叶子节点的左右孩子。 要求进行一系列交换,使得最终所有叶子节点的权值按照遍历序写出来,逆序对个数最少。

输入

第一行\(n\)表示叶子结点个数

接下来每行一个数\(x\)。如果\(x\)为\(0\),表示这个节点为非叶子节点,递归地向下读入其左孩子和右孩子的信息。如果\(x\)不为\(0\),表示这个节点是叶子节点,权值为\(x\)。

输出

输出一行,表示最少逆序对个数。

样例输入

3
0
0
3
1
2

样例输出

1

数据范围

对于前\(10\%\)的数据,\(n \leq 20\)

对于前\(30\%\)的数据,\(n \leq 2000\)

对于\(100\%\)的数据,\(n \leq 200000\)

题解

线段树合并对我来说还是一个新的算法呢\(Ou\ O\)。

首先进行简单分析,发现某一个非叶子节点是否交换无法影响到父亲及以上的节点产生的逆序对,只需要计算出每一个非叶子节点子树内交换与否产生的最小逆序对数量,全部加起来即可(独善其身)。

然后考虑如何计算左右子树哪个产生的逆序对数量,并应考虑到合并左右子树的信息的实现方式。由此可以联想到线段树合并——这样可维护子树的信息并快速合并到父亲。然后考虑如何算答案。我们可以在合并的时候进行计算。对于一个值\(mid\),可以轻而易举地算出来合并中的两子树小于等于或大于它的数量(即\(size\))。左子树大于的和右子树小于等于的乘积即为这一层中不交换的逆序对数量,反之亦然。我们合并时肯定会合并到最后一层,路上就可以计算出交换与否的逆序对数量,两者取最小值加到答案中即可。

\(PS:\)注意\(long\ long\),尤其是\(dfs\)中计算某一个子树的逆序对时。

\(Code:\)

#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);
}

「NOI.AC」Leaves 线段树合并的更多相关文章

  1. LOJ #2537. 「PKUWC 2018」Minimax (线段树合并 优化dp)

    题意 小 \(C\) 有一棵 \(n\) 个结点的有根树,根是 \(1\) 号结点,且每个结点最多有两个子结点. 定义结点 \(x\) 的权值为: 1.若 \(x\) 没有子结点,那么它的权值会在输入 ...

  2. bzoj5518 & loj3046 「ZJOI2019」语言 线段树合并+树链的并

    题目传送门 https://loj.ac/problem/3046 题解 首先问题就是问有多少条路径是给定的几条路径中的一条的一个子段. 先考虑链的做法. 枚举右端点 \(i\),那么求出 \(j\) ...

  3. [NOI.AC#33]bst 线段树

    链接 区间修改,完全二叉树,这引导我们把这棵树看成一棵线段树 .线段树的每一个节点相当于这棵二叉树的节点, 对于区间交换操作,我们对二叉树的每一层从上到下分别考虑,找到L,R在第i层对应的节点修改 这 ...

  4. LOJ #2359. 「NOIP2016」天天爱跑步(倍增+线段树合并)

    题意 LOJ #2359. 「NOIP2016」天天爱跑步 题解 考虑把一个玩家的路径 \((x, y)\) 拆成两条,一条是 \(x\) 到 \(lca\) ( \(x, y\) 最近公共祖先) 的 ...

  5. LOJ 2991 「THUSC 2016」补退选——trie+线段树合并或vector

    题目:https://loj.ac/problem/2291 想了线段树合并的做法.就是用线段树维护 trie 的每个点在各种时间的操作. 然后线段树合并一番,线段树维护前缀最大值,就是维护最大子段和 ...

  6. 「BZOJ2733」「洛谷3224」「HNOI2012」永无乡【线段树合并】

    题目链接 [洛谷] 题解 很明显是要用线段树合并的. 对于当前的每一个连通块都建立一个权值线段树. 权值线段树处理操作中的\(k\)大的问题. 如果需要合并,那么就线段树暴力合并,时间复杂度是\(nl ...

  7. loj#2059. 「TJOI / HEOI2016」字符串 sam+线段树合并+倍增

    题意:给你一个子串,m次询问,每次给你abcd,问你子串sa-b的所有子串和子串sc-d的最长公共前缀是多长 题解:首先要求两个子串的最长公共前缀就是把反过来插入变成最长公共后缀,两个节点在paren ...

  8. LOJ2537. 「PKUWC2018」Minimax【概率DP+线段树合并】

    LINK 思路 首先暴力\(n^2\)是很好想的,就是把当前节点概率按照权值大小做前缀和和后缀和然后对于每一个值直接在另一个子树里面算出贡献和就可以了,注意乘上选最大的概率是小于当前权值的部分,选最小 ...

  9. loj2537 「PKUWC2018」Minimax 【概率 + 线段树合并】

    题目链接 loj2537 题解 观察题目的式子似乎没有什么意义,我们考虑计算出每一种权值的概率 先离散化一下权值 显然可以设一个\(dp\),设\(f[i][j]\)表示\(i\)节点权值为\(j\) ...

随机推荐

  1. 生产者与消费者---demo2---boke

    假设有这样一种情况,有一个桌子,桌子上面有一个盘子,盘子里只能放一颗鸡蛋,A专门往盘子里放鸡蛋,如果盘子里有鸡蛋,则一直等到盘子里没鸡蛋,B专门从盘子里拿鸡蛋,如果盘子里没鸡蛋,则等待直到盘子里有鸡蛋 ...

  2. ECS Windows系统使用自带监视器查看IIS并发连接数

    问题现象 ECS Windows系统如何查看IIS并发连接数? 解决方案 1.运行-->输入“perfmon.msc” . 2.在“系统监视器”图表区域里点击右键,然后点“添加计数器”. 3.在 ...

  3. react核心知识点高度总结

    本文系统的将react的语法以最简练的方式列举出来 安装 写在前面 JSX 组件的定义 state 生命周期 方法 条件渲染 列表 表单 组合嵌套 扩展语法 context传递props 错误拦截 r ...

  4. 第一章 深入Web请求过程(待续)

    B/S网络架构概述 如何发起一个请求 HTTP解析 DNS域名解析 CDN工作机制

  5. DAY18-Django之分页和中间件

    分页 Django的分页器(paginator) view from django.shortcuts import render,HttpResponse # Create your views h ...

  6. DAY11-MYSQL数据备份、pymysql模块

    一 IDE工具介绍 生产环境还是推荐使用mysql命令行,但为了方便我们测试,可以使用IDE工具 下载链接:https://pan.baidu.com/s/1bpo5mqj 掌握: #1. 测试+链接 ...

  7. android opencv

    最近工作需求:用opencv来先做一个demo.扫描照片进行边缘检测和透视矫正. 之后会加入照片降噪等处理. 请教了一下搞图像的同事.他的提议: 1.绿盟的“黄色照片检测” 用的是动态的opencv库 ...

  8. oracle 常用set命令

    SQL> set timing on;           //设置显示“已用时间:XXXX”SQL> set autotrace on;        //设置允许对执行的sql进行分析 ...

  9. CSS中cursor的pointer 与 hand(转)

    CSS中cursor的pointer 与 hand 转载 2015年12月25日 16:18:36 标签: cursorpointer / cursorhand 1781 cursor:hand 与 ...

  10. Hibernate和JPA

    ORM(Object/Relational Mapping : 对象关系映射)就是利用描述对象和数据库之间映射的元数据,自动(且透明)的将java应用程序中的对象持久化到关系数据库的表中.HIbern ...