【YBT2023寒假Day12 B】仰望星空(DP)(线段树)(笛卡尔树)

news/2024/6/27 5:22:19 标签: DP, 线段树, 笛卡尔树

仰望星空

题目链接:YBT2023寒假Day12 B

题目大意

有一个 n*n 的网格,第 i 列下面的 ai 个点都是障碍。
然后又一些不是障碍的地方有特殊点,删掉它有费用。
要你用最小费用使得不存在两个特殊点在一个矩阵中且矩阵中没有障碍。

思路

注意到障碍很特殊,考虑从这里开始思考。
会发现两个特殊点可以同时存在的条件是它们所在的列形成的区间 a i a_i ai 的最大值要大于行小的那个。

那你可以考虑保留那些点,使得每对都是这样的,而且保留下的费用最大。
那这个最大值不难想到笛卡尔树
考虑对于笛卡尔树上的一个点,它的区间是 l ∼ r l\sim r lr,那个最大值是 x x x,它父亲的最大值是 y y y(如果没有父亲那 y = N y=N y=N)。
那对于 [ l ∼ r ] [ x + 1 , y ] [l\sim r][x+1,y] [lr][x+1,y] 是一个空的,这里可以放一个点。

那考虑放或者不放。
不放的话,那问题就直接变成两个子树的答案和。
放的话,那如果它放在了第 i i i 列,那我们一直到属于第 i i i 列的最底下的点,路径上的所有点都不能放了。
那我们就要把剩下的子树的 DP 值加起来得到答案。

那至于怎么加,我们可以发现要加的子树是这条链上每个点的另一个兄弟的答案。
那我们除了 f x f_x fx 表示 i i i 子树的答案,再设一个 f x ′ f'_x fx 是它兄弟的答案,那我们用一个线段树,每次给 x x x 代表的区间加上贡献,询问就是单点查询了(所以树状数组也行)。

代码

#include<set>
#include<cstdio>
#include<algorithm>
#define ll long long

using namespace std;

const int N = 2e5 + 100; 	
struct node {
	int x, y, c;
}b[N];
int n, a[N], m, id[N];
set <int> s;
ll ans;

bool cmpa(int x, int y) {
	return a[x] < a[y];
}

bool cmpb(node x, node y) {
	if (x.y != y.y) return x.y < y.y;
	return x.x < y.x;
}

struct XD_tree {
	ll f[N << 2];
	
	void update(int now, int l, int r, int L, int R, ll x) {
		if (L > R) return ;
		if (L <= l && r <= R) {
			f[now] += x; return ;
		}
		int mid = (l + r) >> 1;
		if (L <= mid) update(now << 1, l, mid, L, R, x);
		if (mid < R) update(now << 1 | 1, mid + 1, r, L, R, x);
	}
	
	ll query(int now, int l, int r, int pl) {
		if (l == r) return f[now];
		int mid = (l + r) >> 1;
		if (pl <= mid) return f[now] + query(now << 1, l, mid, pl);
			else return f[now] + query(now << 1 | 1, mid + 1, r, pl);
	}
}T;

int main() {
	freopen("starry.in", "r", stdin);
	freopen("starry.out", "w", stdout);
	
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) scanf("%d", &a[i]), id[i] = i;
	sort(id + 1, id + n + 1, cmpa);
	scanf("%d", &m);
	for (int i = 1; i <= m; i++) {
		scanf("%d %d %d", &b[i].x, &b[i].y, &b[i].c);
	}
	sort(b + 1, b + m + 1, cmpb);
	
	for (int i = 0; i <= n + 1; i++) s.insert(i);
	int L = 1;
	for (int i = 1; i <= m; i++) {
		while (L <= n && a[id[L]] < b[i].y) s.erase(s.find(id[L])), L++;
		ll no = b[i].c, yes = T.query(1, 1, n, b[i].x);
		if (no < yes) {
			ans += no;
		}
		else {
			ans += yes;
			set <int> ::iterator it = s.lower_bound(b[i].x);
			int r = (*it) - 1, l = (*(--it)) + 1;
			T.update(1, 1, n, l, r, -yes + b[i].c);//撤销选的操作并补回不选的费用
		}
		
	}
	printf("%lld", ans);
	
	return 0;
}

http://www.niftyadmin.cn/n/89339.html

相关文章

MySQL盲点知识,索引命中分析和长度的计算

我发现啊&#xff0c;大家都喜欢默默收藏文章&#xff0c;每天六七十次收藏&#xff0c;但是就不点赞 大家的点赞和互动是我更文的动力 /(ㄒoㄒ)/所以我决定发起一项活动&#xff0c;到三月三十一日统计&#xff0c;留言次数和赞赏次数最多的人各将获得赠书一本。点击上方“后端…

oracle中怎么创建存储过程

在oracle中&#xff0c;可以使用“CREATE PROCEDURE”语句来创建存储过程&#xff0c;基本语法“CREATE [OR REPLACE] PROCEDURE 过程名 [参数列表] IS [...] BEGIN ... END [过程名];”。 什么是存储过程&#xff1f; 所谓存储过程(Stored Procedure)&#xff0c;就是一组…

Kerberos认证原理与使用教程

Kerberos认证原理与使用教程 一、Kerberos 概述 二、什么是 Kerberos ​ Kerberos 是一种计算机网络认证协议&#xff0c;用来在非安全网络中&#xff0c;对个人通信以安全的手段进行身份认证。这个词又指麻省理工学院为这个协议开发的一套计算机软件。软件设计上采用客户端…

小学生学Arduino---------点阵(二)动态图片以及文字

今天进阶了利用人眼视觉暂留原理制作动态的图片变换。 1、熟练掌握图片显示器的使用 2、创作多种动态图片、文字的显示 3、明确动态图片、文字显示过程 4、掌握图片显示器中清空指令的使用 5、搭建动态图片、文字的显示电路 6、编写动态图片、文字的程序 复习&#xff1a; 绘…

面向对象的程序设计C++课堂复盘总结 C语言复习+C++基础语法

Stay Hungry&#xff0c;Stay Foolish. 任何人都能写出机器能看懂的代码&#xff0c;但只有优秀的程序员才能写出人能看懂的代码。 有两种写程序的方式&#xff1a;一种是把代码写得非常复杂&#xff0c;以至于 “看不出明显的错误”&#xff1b;另一种是把代码写得非常简单&am…

LU定时器T3212介绍

前面文档介绍过LU accepted and Rejected流程。 LU Accepted or Rejected过程介绍 (qq.com) LU流程中,MS通过定时器T3212控制LU发起周期,定时器T3212值在SIB3的广播消息中。 T3212:SIB3消息关键Log [NW->MS] RR__SI_3 //T3212:0

电子作业指导书系统能树立良好的生产形象

“制造”就是以规定的成本、规定的工时、生产出品质均匀、符合规格的产品。从全球新能源汽车的发展来看&#xff0c;其动力电源主要包括锂离子电池、镍氢电池、铅酸电池、超级电容器&#xff0c;其中超级电容器大多以辅助动力源的形式出现。那么&#xff0c;电子作业指导书系统…

【C++】string类的基本使用

层楼终究误少年&#xff0c;自由早晚乱余生。你我山前没相见&#xff0c;山后别相逢… 文章目录一、编码&#xff08;ascll、unicode字符集、常用的utf-8编码规则、GBK&#xff09;1.详谈各种编码规则2.汉字在不同的编码规则中所占字节数二、string类的基本使用1.string类的本质…