P5504 [JSOI2011]柠檬
时间:2019-08-21
本文章向大家介绍P5504 [JSOI2011]柠檬,主要包括P5504 [JSOI2011]柠檬使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
显然考虑 $dp$ ,发现从右往左和从左往右是一样的,所以只考虑一边就行
发现对于切的左右端点,选择的 $s0$ 一定要为左右端点的柠檬大小,不然这个端点不产生贡献还不如分开来单个贡献
所以设 $f[i]$ 表示当前把 $1$ 到 $i$ 的都切了,产生的最大贡献,设 $c[i]$ 表示位置 $i$ 及之前大小为 $s[i]$ 的柠檬个数,有转移:
$f[i]=f[j-1]+s[i](c[i]-c[j]+1)^2,j \in [1,i]$,并且要满足 $s[i]=s[j]$ ,发现是个斜率优化的式子,拆开来:
$f[i]=f[j-1]+s[i](c[i]^2-2c[i](c[j]-1)+(c[j]-1)^2)$,再拆,变成
$f[j-1]+s[i](c[j]-1)^2=2s[i]c[i](c[j]-1)+f[i]-s[i]c[i]^2$,因为转移都是在同一个大小之间转移,所以 $s[i]$ 可以看成常数
所以 $y=f[j-1]+s[i](c[j]-1)^2$,$k=2s[i]c[i]$,$x=c[j]-1$,$b=f[i]-s[i]c[i]^2$,对每种 $s$ 都维护一个凸包即可
显然对于同一个 $s$, $k,x$ 都单调递增,并且求 $max$ ,所以维护上凸包
插点时从右边插,更新 $f$ 时也切凸包右边,用 $vector$ 维护凸包即可
注意先加当前点再更新 $f$($j \in [1,i]$)
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<vector> using namespace std; typedef long long ll; typedef long double ldb; 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; } const int N=2e5+7,M=2e4+7; int n,s[N],c[N],cnt[M]; ll f[N]; struct Vec{//向量 ldb x,y; Vec (ll a=0,ll b=0) { x=a,y=b; } inline ldb operator * (const Vec &tmp) const { return x*tmp.y-y*tmp.x; } }; struct Poi{//凸包点 ll f; int cj,s; Poi (ll a=0,int b=0,int c=0) { f=a,cj=b,s=c; } inline ll calc(int i) { return f+1ll*s*(c[i]-cj+1)*(c[i]-cj+1); } inline ll X() { return 1ll*s*(cj-1); } inline ll Y() { return f+1ll*s*(cj-1)*(cj-1); } }; inline Vec operator - (Poi &A,Poi &B) { return Vec( A.X()-B.X() , A.Y()-B.Y() ); } vector <Poi> st[M];//每种s维护凸包 int main() { n=read(); for(int i=1;i<=n;i++) s[i]=read(),c[i]=++cnt[s[i]]; for(int i=1;i<=n;i++) { Poi t(f[i-1],c[i],s[i]); int len=st[s[i]].size()-1; while( len>0 && (st[s[i]][len]-st[s[i]][len-1])*(t-st[s[i]][len-1])>=0 ) st[s[i]].pop_back(),len--; st[s[i]].push_back(t); len++;//先插入 while( len>0 && st[s[i]][len].calc(i) <= st[s[i]][len-1].calc(i) ) st[s[i]].pop_back(),len--; f[i]=st[s[i]][len].calc(i);//再更新 } printf("%lld\n",f[n]); return 0; }
原文地址:https://www.cnblogs.com/LLTYYC/p/11387738.html
- python文件打开方式详解——a、a+、r+、w+区别
- 三封报警邮件的分析(r6笔记第95天)
- HCTF2017 部分 Web 出题思路详解
- 基于springboot+kotlin+gradle构建的框架的坑
- 创建Task的多种方法
- 运行map()后,报:<map object at 0x02629E50>解决方法与原因分析
- Gradle的快速入门
- Python笔记从html中提取字段
- 一条细小的报警短信的处理(r6笔记第96天)
- 1.react的基础知识
- 防火墙设置的小问题(r6笔记第94天)
- 有没有必要把机器学习算法自己实现一遍?
- python中从str中提取元素到list以及将list转换为str
- 简单易学的机器学习算法——线性回归(2)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Spring AOP动态代理原理与实现方式
- 基于注解多数据源解决方案
- Java并发编程:CountDownLatch、CyclicBarrier和Semaphore
- 你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它?
- java阻塞队列得实现
- 谈谈如何利用 valgrind 排查内存错误
- 用java写一个死锁
- Runnable和Thread比较
- 使用@ConditionalOnProperty注解
- Java注解Annotation与自定义注解详解
- spring boot 配置多个DispatcherServlet
- 基于Pytorch实现的MASR中文语音识别
- Quartz.NET 配置文件详解
- 如何用redis正确实现分布式锁?
- SQL Server 事务隔离级别