当前位置: 首页 > news >正文

洛谷 P7302 [NOI1998] 免费的馅饼

在这里插入图片描述

算法分析:

首先,最基础的想法是暴力 DP 复杂度 O ( n 2 ) O(n^2) O(n2)。设 f [ i ] f[i] f[i] 表示在前 i i i 个馅饼中,获得第 i i i 个馅饼的最大得分。转移方程 f [ i ] = max ⁡ ( f [ i ] , f [ j ] + v [ i ] ) f[i] = \max(f[i],f[j]+v[i]) f[i]=max(f[i],f[j]+v[i]),其中 0 < j < i 0<j<i 0<j<i a b s ( p [ i ] , p [ j ] ) ≤ 2 × ( t [ i ] − t [ j ] ) abs(p[i],p[j]) \leq 2 \times (t[i]-t[j]) abs(p[i],p[j])2×(t[i]t[j])。这个转移复杂度是 O ( n 2 ) O(n^2) O(n2)

考虑把绝对值打开,式子就变为

{ p [ i ] − 2 × t [ i ] ≤ p [ j ] − 2 × t [ j ] ( p [ i ] ≤ p [ j ] ) p [ i ] + 2 × t [ i ] ≥ p [ j ] + 2 × t [ j ] ( p [ i ] ≥ p [ j ] ) \begin{cases}p[i] - 2 \times t[i] \leq p[j] - 2 \times t[j] (p[i] \leq p[j])\\p[i] + 2 \times t[i] \geq p[j] + 2 \times t[j] (p[i] \geq p[j])\end{cases} {p[i]2×t[i]p[j]2×t[j](p[i]p[j])p[i]+2×t[i]p[j]+2×t[j](p[i]p[j])

那么这个式子分成两种情况,维护比较麻烦。但我们发现,如果要的那个式子满足了,另一个也会满足。所以我们让两个式子都满足。为什么呢?举个例子,假如满足了第一个式子,那么移项可以得到 p [ i ] − p [ j ] ≤ 2 × t [ i ] − 2 × t [ j ] p[i] - p[j] \leq 2 \times t[i] - 2 \times t[j] p[i]p[j]2×t[i]2×t[j],而满足这个式子的条件是 p [ i ] ≤ p [ j ] ⇒ p [ i ] − p [ j ] ≤ 0 ⇒ t [ i ] ≥ t [ j ] p[i] \leq p[j] \Rightarrow p[i] - p[j] \leq 0 \Rightarrow t[i] \geq t[j] p[i]p[j]p[i]p[j]0t[i]t[j]。然后我们看第二个式子, p [ i ] ≥ p [ j ] p[i] \geq p[j] p[i]p[j] 并且 t [ i ] ≥ t [ j ] t[i] \geq t[j] t[i]t[j],显然是满足条件的。所以第一个式子满足,第二个式子也满足。

对于第一个式子,我们可以在读入的时候维护,并进行排序。这里问题就相当于变成了一个二维偏序。第二维由于数组开不下,我们需要进行离散化。

存两个关键值, a [ i ] . k e y 1 = a [ i ] . p − 2 × a [ i ] . t a[i].key1 = a[i].p - 2 \times a[i].t a[i].key1=a[i].p2×a[i].t a [ i ] . k e y 2 = a [ i ] . p + 2 × a [ i ] . t a[i].key2 = a[i].p + 2 \times a[i].t a[i].key2=a[i].p+2×a[i].t。我们先按 k e y 2 key2 key2 从小到大排序,并进行离散化。

inline bool cmp2(node a,node b) { return a.key2 < b.key2; }

sort(a+1,a+n+1,cmp2);
sort(lsh+1,lsh+n+1);
unique(lsh+1,lsh+n+1)-lsh;
rep(i,1,n) a[i].key2 = lower_bound(lsh+1,lsh+n+1,a[i].key2)-lsh;

然后我们按 k e y 1 key1 key1 从大到小进行排序,相当于先把第一个条件给固定。对于 k e y 2 key2 key2 进行操作。

先来看为什么对 k e y 1 key1 key1 k e y 2 key2 key2 要这么排序。对于 k e y 1 key1 key1,从大到小排序,在 j < i j < i j<i 的时候需要满足 a [ i ] . k e y 1 < a [ j ] . k e y 1 a[i].key1 < a[j].key1 a[i].key1<a[j].key1 k e y 2 key2 key2 是从小到大排序的,在 j < i j<i j<i 的时候需要满足 a [ i ] . k e y 2 > a [ j ] . k e y 2 a[i].key2 > a[j].key2 a[i].key2>a[j].key2所以我们发现,两个式子同时满足可以从线段树的前面转移到后面。

我们在线段树中存区间的最大值。先在线段树中求出 1 1 1 a [ i ] . k e y 2 a[i].key2 a[i].key2 的最大值,加上 a [ i ] . v a[i].v a[i].v,然后存入线段树中 a [ i ] . k e y 2 a[i].key2 a[i].key2 的位置。最后的答案就是 t [ 1 ] t[1] t[1]

一个单点修改,区间查询的线段树即可。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define re register
#define drep(a,b,c) for(re int a(b) ; a>=(c) ; --a)
#define rep(a,b,c) 	for(re int a(b) ; a<=(c) ; ++a)
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
inline void print(int x){
	if(x < 0) putchar('-'),x = -x;
	if(x >= 10) print(x / 10);
	putchar(x % 10 + '0');
}
const int M = 1e5+10;
struct node{
	int t,p,v;
	int key1,key2;
}a[M];
inline bool cmp1(node a,node b) { return a.key1 < b.key1; }
inline bool cmp2(node a,node b) { return a.key2 < b.key2; }
int lsh[M],tot,n,w;
#define ls (k<<1)
#define rs (k<<1|1)
int t[M<<2];
inline void pushup(int k) { t[k] = max(t[ls],t[rs]); }
inline void modify(int k,int l,int r,int x,int v){
	if(l == x && r == x){
		t[k] = v;
		return;
	}
	int mid = (l+r)>>1;
	if(x <= mid) modify(ls,l,mid,x,v);
	else modify(rs,mid+1,r,x,v);
	pushup(k);
}
inline int query(int k,int l,int r,int x,int y){
	if(x <= l && r <= y) return t[k];
	int res = 0;
	int mid = (l+r)>>1;
	if(x <= mid) res = max(res,query(ls,l,mid,x,y));
	if(y > mid)  res = max(res,query(rs,mid+1,r,x,y));
	return res;
}
signed main(){
	w = read(),n = read();
	rep(i,1,n){
		scanf("%d%d%d",&a[i].t,&a[i].p,&a[i].v);
		a[i].key1 = a[i].p - 2*a[i].t;
		lsh[i] = a[i].key2 = a[i].p + 2*a[i].t;
	}
	sort(a+1,a+n+1,cmp2);
	sort(lsh+1,lsh+n+1);
	unique(lsh+1,lsh+n+1)-lsh;
	rep(i,1,n) a[i].key2 = lower_bound(lsh+1,lsh+n+1,a[i].key2)-lsh;
	sort(a+1,a+n+1,cmp1);
	rep(i,1,n){
		int res = query(1,1,n,a[i].key2,n) + a[i].v;
		modify(1,1,n,a[i].key2,res);
	}
	printf("%d\n",t[1]);
	return 0;
}

相关文章:

  • Docker基础-2.常用命令与Docker镜像
  • Java的Lambda表达式学习笔记:认识lambda表达式
  • SAP Spartacus 项目开发时需要注意的一些常见错误
  • SpringBoot - @JsonIgnore和@JsonIgnoreProperties注解详解以及区别
  • 神经网络概念图片大全,神经网络概念图片解析
  • 产业园区构建公共服务平台应当包含哪些服务
  • 链动2+1模式是如何驱动品牌全面爆发的?
  • DMSA(Distributed multi-scenario analysis)
  • 从IDEA开始,迈进GO语言之门
  • 神经网络算法有哪些模型,神经网络算法模型resnet
  • 管理Java依赖关系的最佳实践
  • DevOps 团队如何防御 API 攻击
  • Dubbo管理控制台dubbo-admin搭建
  • 一觉醒来发现Github要废弃Trending Tab
  • python+停车管理系统 毕业设计-附源码271400
  • [微信小程序] 使用ES6特性Class后出现编译异常
  • canvas实际项目操作,包含:线条,圆形,扇形,图片绘制,图片圆角遮罩,矩形,弧形文字...
  • Druid 在有赞的实践
  • Java 23种设计模式 之单例模式 7种实现方式
  • LintCode 31. partitionArray 数组划分
  • MYSQL如何对数据进行自动化升级--以如果某数据表存在并且某字段不存在时则执行更新操作为例...
  • PHP 的 SAPI 是个什么东西
  • react-native 安卓真机环境搭建
  • Traffic-Sign Detection and Classification in the Wild 论文笔记
  • Travix是如何部署应用程序到Kubernetes上的
  • 关于extract.autodesk.io的一些说明
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 使用 5W1H 写出高可读的 Git Commit Message
  • 微信小程序:实现悬浮返回和分享按钮
  • 文本多行溢出显示...之最后一行不到行尾的解决
  • ​什么是bug?bug的源头在哪里?
  • ###51单片机学习(2)-----如何通过C语言运用延时函数设计LED流水灯
  • #Js篇:单线程模式同步任务异步任务任务队列事件循环setTimeout() setInterval()
  • #考研#计算机文化知识1(局域网及网络互联)
  • #图像处理
  • (BFS)hdoj2377-Bus Pass
  • (ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类
  • (Redis使用系列) SpringBoot中Redis的RedisConfig 二
  • (待修改)PyG安装步骤
  • (二)windows配置JDK环境
  • (附源码)ssm高校运动会管理系统 毕业设计 020419
  • (附源码)基于SSM多源异构数据关联技术构建智能校园-计算机毕设 64366
  • (附源码)计算机毕业设计SSM在线影视购票系统
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理 第13章 项目资源管理(七)
  • (转)关于如何学好游戏3D引擎编程的一些经验
  • (转)如何上传第三方jar包至Maven私服让maven项目可以使用第三方jar包
  • 、写入Shellcode到注册表上线
  • .NET Core6.0 MVC+layui+SqlSugar 简单增删改查
  • .NET版Word处理控件Aspose.words功能演示:在ASP.NET MVC中创建MS Word编辑器
  • .NET框架类在ASP.NET中的使用(2) ——QA
  • /proc/stat文件详解(翻译)
  • ?
  • @CacheInvalidate(name = “xxx“, key = “#results.![a+b]“,multi = true)是什么意思
  • [Angular] 笔记 20:NgContent
  • [BUUCTF]-Reverse:reverse3解析