前言

线段树+区间DP题,线段树却不是优化DP的,是不是很意外?

题面

二叉搜索树是一种二叉树,每个节点都有一个权值,并且一个点的权值比其左子树里的点权值都大,比起右子树里的点权值都小。

一种朴素的向二叉搜索树中插入节点的算法是,将新节点作为一个新的叶子节点插入树中,维持二叉搜索树的性质,并且不移动原有的节点。

现在有一个长度为

n

n

n 的排列

a

a

a,你可以任意重排第

l

l

l 到第

r

r

r 个数,但不移动其余数。接下来依次从

1

1

1 到

n

n

n 将

a

i

a_i

ai​ 作为新节点的权值插入一棵本来为空的二叉搜索树。试问得到的二叉搜索树所有节点的深度和最小是多少?(假定根节点的深度为

1

1

1)。

输入

第一行一个数

n

n

n ,(

1

n

1

0

5

1\leq n\leq 10^5

1≤n≤105)。
接下来一行一个排列

a

a

a 。
最后一行两个数

l

l

l,

r

r

r,(

1

r

l

+

1

200

1\leq r-l+1\leq 200

1≤r−l+1≤200)。

输出

一行一个答案。

题解

所有节点的深度和,等于所有子树大小之和。这样想

l

r

l\sim r

l∼r 以内的变动就不会影响

r

r

r 后面的点了。

我们再分析往二叉搜索树中添加节点的过程,新点要成为叶子,并且在原数列的正确位置。假设当前数

x

x

x 的前一个数为

L

L

L ,后一个为

R

R

R ,我们会发现

L

+

1

R

1

L+1\sim R-1

L+1∼R−1 以内的数都一定在

x

x

x 的子树中了,而且只有这些数在

x

x

x 的子树中。后者很好理解,前者是因为,该范围内除了

x

x

x 以外的数都比

x

x

x 晚成为叶子,因此若成为了

L

L

L 或

R

R

R 的后代便会矛盾。

所以,加点操作可以转化为:在一个数轴对应位置上加一个点,记前一个点位置为

L

L

L ,后一个点位置为

R

R

R ,对答案贡献

R

L

1

R-L-1

R−L−1 ,维护前后位置是经典的线段树操作了,当然也可以直接用 set ,快捷得多。

我们考虑

1

R

1\sim R

1∼R 加完后的序列,如果

L

R

L\sim R

L∼R 的点此时两两不相邻,那我们就没得变了,答案都是确定的了,因为每个点的贡献都取决于

1

L

1

1\sim L-1

1∼L−1 的点了。但是,如果存在相邻的一段(

L

R

L\sim R

L∼R 的点),我们就有的选了,可以通过安排这些点的加入顺序来改变贡献。假设有相邻的一段

m

m

m 个点,分割出

m

+

1

m+1

m+1 个线段,我们的操作就是每次把相邻的两条线段合并成一条线段,并算上这条线段的长度作为贡献,直到最后只有一条线段。

是不是很像合并果子?但是不一样,我们只能对相邻的果子进行操作。于是只能用区间 DP,这就是数据范围

r

l

+

1

200

r-l+1\leq200

r−l+1≤200 的作用,时间复杂度

O

(

l

e

n

g

t

h

3

)

O({\rm length}^3)

O(length3) 能过。我们需要对形成的每段连续点分别进行区间 DP 。

总时间复杂度

O

(

n

log

n

+

(

r

l

+

1

)

3

)

O(n\log n+(r-l+1)^3)

O(nlogn+(r−l+1)3) 。

CODE

#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<ctime>
#include<queue>
#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
#define LL long long
#define DB double
#define ENDL putchar('\n')
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
#define SI(x) multiset<x>::iterator
#define MI map<int,int>::iterator
#define eps (1e-4)
LL read() {
LL f=1,x=0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f*x;
}
void putpos(LL x) {
if(!x) return ;
putpos(x/10); putchar('0'+(x%10));
}
void putnum(LL x) {
if(!x) putchar('0');
else if(x < 0) putchar('-'),putpos(-x);
else putpos(x);
}
void AIput(LL x,char c) {putnum(x);putchar(c);} int n,m,s,o,k;
int L,R;
struct it{
int l,r;it(){l=0x3f3f3f3f,r=-1;}
it(int L,int R){l=L;r=R;}
}tre[MAXN<<2];
it merg(it a,it b) {return it(min(a.l,b.l),max(a.r,b.r));}
int M;
void maketree(int n) {
M=1;while(M<n+2) M<<=1;
for(int i = M+1;i <= M+n;i ++) tre[i] = it(n+1,0);
for(int i = M-1;i > 0;i --) tre[i] = merg(tre[i<<1],tre[i<<1|1]);
}
void addtree(int x,it y) {
int s = M+x;tre[s] = y;s >>= 1;
while(s) tre[s] = merg(tre[s<<1],tre[s<<1|1]),s >>= 1;
}
it findtree(int l,int r) {
if(l > r) return it(r+1,l-1);
it as = it();
int s = M+l-1,t = M+r+1;
while(s || t) {
if((s>>1) != (t>>1)) {
if(!(s&1)) as = merg(as,tre[s^1]);
if(t & 1) as = merg(as,tre[t^1]);
}else break;
s >>= 1;t >>= 1;
}return as;
}
int findl(int x) {
it as = findtree(1,x-1); return as.r;
}
int findr(int x) {
it as = findtree(x+1,n); return as.l;
}
int a[MAXN];
LL dp[205][205],sum[205];
LL DP(int *s,int n) {
if(n < 1) return 0ll;
sum[0] = 0ll;
for(int i = 1;i <= n;i ++) {
sum[i] = sum[i-1] + s[i];
dp[i][i] = 0;
}
for(int i = 2;i <= n;i ++) {
for(int j = i-1;j > 0;j --) {
dp[j][i] = 1e18;
for(int k = j;k < i;k ++) {
dp[j][i] = min(dp[j][i],dp[j][k] + dp[k+1][i]);
}
dp[j][i] += sum[i] - sum[j-1] + i-j;
}
}
return dp[1][n];
}
int main() {
n = read();
for(int i = 1;i <= n;i ++) {
a[i] = read();
}
maketree(n);
L = read();R = read();
LL ans = 0;
for(int i = 1;i <= L-1;i ++) {
addtree(a[i],it(a[i],a[i]));
int ll = findl(a[i]),rr = findr(a[i]);
ans += rr-ll-1;
}
sort(a + L,a + R + 1);
for(int i = L;i <= R;i ++) addtree(a[i],it(a[i],a[i]));
int tm[205] = {},cntm = 0;
for(int i = L;i <= R;i ++) {
int j = i;
cntm = 0;
tm[++ cntm] = a[i] - findl(a[i]) - 1;
while(j < R && findl(a[j+1]) == a[j]) {
j ++; tm[++ cntm] = a[j] - findl(a[j]) - 1;
}
tm[++ cntm] = findr(a[j]) - a[j] - 1;
ans += DP(tm,cntm);
i = j;
}
for(int i = R+1;i <= n;i ++) {
addtree(a[i],it(a[i],a[i]));
int ll = findl(a[i]),rr = findr(a[i]);
ans += rr-ll-1;
}
printf("%lld\n",ans);
return 0;
}

二叉搜索树TREE(线段树,区间DP)的更多相关文章

  1. 二叉搜索树 [四边形不等式优化区间dp]

    二叉搜索树 [四边形不等式优化区间dp] 题目描述 有 \(n\) 个结点,第 \(i\) 个结点的权值为 \(i\) . 你需要对它们进行一些操作并维护一些信息,因此,你需要对它们建立一棵二叉搜索树 ...

  2. [LeetCode] 538. 把二叉搜索树转换为累加树 ☆(中序遍历变形)

    把二叉搜索树转换为累加树 描述 给定一个二叉搜索树(Binary Search Tree),把它转换成为累加树(Greater Tree),使得每个节点的值是原来的节点值加上所有大于它的节点值之和. ...

  3. 从二叉搜索树到AVL树再到红黑树 B树

    这几种树都属于数据结构中较为复杂的,在平时面试中,经常会问理解用法,但一般不会问具体的实现,所以今天来梳理一下这几种树之间的区别与联系,感谢知乎用户@Cailiang,这篇文章参考了他的专栏. 二叉查 ...

  4. Java实现 LeetCode 538 把二叉搜索树转换为累加树(遍历树)

    538. 把二叉搜索树转换为累加树 给定一个二叉搜索树(Binary Search Tree),把它转换成为累加树(Greater Tree),使得每个节点的值是原来的节点值加上所有大于它的节点值之和 ...

  5. [Swift]LeetCode538. 把二叉搜索树转换为累加树 | Convert BST to Greater Tree

    Given a Binary Search Tree (BST), convert it to a Greater Tree such that every key of the original B ...

  6. 538 Convert BST to Greater Tree 把二叉搜索树转换为累加树

    给定一个二叉搜索树(Binary Search Tree),把它转换成为累加树(Greater Tree),使得每个节点的值是原来的节点值加上所有大于它的节点值之和.例如:输入: 二叉搜索树:     ...

  7. Leetcode 538. 把二叉搜索树转换为累加树

    题目链接 https://leetcode.com/problems/convert-bst-to-greater-tree/description/ 题目描述 大于它的节点值之和. 例如: 输入: ...

  8. LeetCode 把二叉搜索树转换为累加树

    第538题 给定一个二叉搜索树(Binary Search Tree),把它转换成为累加树(Greater Tree),使得每个节点的值是原来的节点值加上所有大于它的节点值之和. 例如: 输入: 二叉 ...

  9. 数据结构中的树(二叉树、二叉搜索树、AVL树)

    数据结构动图展示网站 树的概念 树(英语:tree)是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合.它是由n(n>=1)个有限节点组成一个具有 ...

随机推荐

  1. 修改mysql数据库存储路径

    最近一段比较忙,所以一直没有及时的更新总结一下测试路上遇到的问题,今天先来分享一下如何修改mysql存储路径(场景:在自己电脑上搭建的服务器上安装mysql,二.在公司自己的服务器上搭建mysql数据 ...

  2. .NET C#基础(6):命名空间 - 组织代码的利器

    0. 文章目的   面向C#新学者,介绍命名空间(namespace)的概念以及C#中的命名空间的相关内容 1. 阅读基础   理解C与C#语言的基础语法 2. 名称冲突与命名空间 2.1 一个生活例 ...

  3. Docker安装Jenkins打包Maven项目为Docker镜像并运行【保姆级图文教学】

    一.前言 Jenkins作为CI.CD的先驱者,虽然现在的风头没有Gitlab强了,但是还是老当益壮,很多中小公司还是使用比较广泛的.最近小编经历了一次Jenkins发包,感觉还不错,所以自己学习了一 ...

  4. 2021蓝桥杯省赛C++A组试题E 回路计数 状态压缩DP详细版

    2021蓝桥杯省赛C++A组试题E 回路计数 状态压缩DP 题目描述 蓝桥学院由21栋教学楼组成,教学楼编号1到21.对于两栋教学楼a和b,当a和b互质时,a和b之间有一条走廊直接相连,两个方向皆可通 ...

  5. 【题解】Codeforces Round #798 (Div. 2)

    本篇为 Codeforces Round #798 (Div. 2) 也就是 CF1689 的题解,因本人水平比较菜,所以只有前四题 A.Lex String 题目描述 原题面 给定两个字符串 \(a ...

  6. Node.js精进(3)——流

    在 JavaScript 中,一般只处理字符串层面的数据,但是在 Node.js 中,需要处理网络.文件等二进制数据. 由此,引入了Buffer和Stream的概念,两者都是字节层面的操作. Buff ...

  7. 分享|智慧环保-生态文明信息化解决方案(附PDF)

    内容摘要: 生态文明建设被提到前所未有的战略高度,我们既要绿水青山,也要金山银山.宁要绿水青山,不要金山银山,而且绿水青山就是金山银山.要正确处理好经济发展同生态环境保护的关系,牢固树立保护生态环境就 ...

  8. Python自动化办公:批量将文件按分类保存,文件再多,只需一秒钟解决

    序言 (https://jq.qq.com/?_wv=1027&k=GmeRhIX0) 当我们电脑里面的文本或者或者文件夹太多了,有时候想找到自己想要的文件,只能通过去搜索文件名,要是名字忘记 ...

  9. 01. DOCKER - 容器技术

    什么是容器 对于容器这个词,大部分人第一时间想到的肯定是生活中常见瓶瓶罐罐,用来装水的东西.它给人的第一感觉就是能 "装". 而在 IT 领域,Container 就被直译为容器, ...

  10. 5-12 RabbitMQ 消息队列

    RabbitMQ 什么是RabbitMQ RabbitMQ 是一个由 Erlang 语言开发的 AMQP 的开源实现. AMQP :Advanced Message Queue,高级消息队列协议.它是 ...