Description

  

  传送门

  

  题意简述:将序列划分成任意多段,从每一段选出一个数\(x\),获得\(在这一段出现的次数x*(x在这一段出现的次数)\)的贡献。求总贡献最大值。

  

  

  

  

  

Solution

  

​  首先,要发现一个很重要的性质:如果某一段选了\(x\),那么这一段一定是以\(x\)开头、以\(x\)结尾的一段。否则,可以将此段缩减至以\(x\)开头、以\(x\)结尾的更小的一段,虽然贡献没有变,但留给其他段的机会更多。

  

  设\(f_i\)表示\(1...i\)的贡献最大值。记\(a_i\)表示\(i\)的数值,\(b_i\)表示\(a_i\)在相同的值中是第几个出现的。显然如果要从别的\(f_j\)转移到\(f_i\),必须满足\(a_{j+1}==a_i\)。我们有转移方程:

\[f_i=\max \{f_{j-1}+a_i(b_i-b_j+1)^2\}\;\;\;j\le i,a_j=a_i
\]

  设\(j\)为最优转移点:

\[\begin{aligned}
f_i&=f_{j-1}+a_i(b_i-(b_j-1))^2\\
f_i&=f_{j-1}+a_i(b_i^2-2b_i(b_j-1)+(b_j-1)^2)\\
f_i&=f_{j-1}+a_ib_i^2-2a_ib_i(b_j-1)+a_i(b_j-1)^2\\
f_{j-1}+a_i(b_j-1)^2&=2a_ib_i(b_j-1)+f_i-a_ib_i^2
\end{aligned}
\]

  这其实是一个直线的式子:\(k=2a_ib_i\),\(x=(b_j-1)\),\(b=(f_i-a_ib_i^2)\),\(y=f_{j-1}+a_i(b_j-1)^2\).

  

  其中\(a_i\)看似和\(i\)有关,无法继续推理。但由于转移的\(j\)满足\(a_j=a_i\),所以每一个位置的数在参与上述DP时,相关联的\(a\)其实就是每一个元素自己的数值,是一个定值。

  

  把每一个元素看成二维平面的一个点\((x,y)\)。由于最优转移相当于最大化截距,那么最优转移点\(j\)可以看做在斜率为\(k\)的时候上凸包碰到的第一个点。

  

  那么我们扫描序列时,维护每一个数值对应的上凸包,每次查询时在上面二分即可。

  

  时间复杂度\(\mathcal O(n \lg n)\)。

  

  当然,也可以用斜率优化直接做。

  

  

  

Code

  

#include <cstdio>
#include <vector>
#define k(i) (2LL*a[i]*b[i])
#define x(i) (b[i]-1LL)
#define y(i) (f[i-1]+1LL*a[i]*(b[i]-1)*(b[i]-1))
#define b(i) (f[i]-1LL*a[i]*b[i]*b[i])
#define pb push_back
#define db pop_back
using namespace std;
typedef long long ll;
const int N=100005,S=10005;
const double EPS=1e-6;
int n,a[N],ecnt[S],b[N];
ll f[N];
vector<int> s[S];
int slen[S];
double slope(int u,int v){return 1.0*(y(v)-y(u))/(x(v)-x(u));}
int query(int col,int k){
k=2*col*k;
int l=0,r=slen[col]-2,mid;
while(l<=r){
mid=(l+r)>>1;
if(slope(s[col][mid],s[col][mid+1])-EPS<=k) r=mid-1;
else l=mid+1;
}
return s[col][l];
}
void insert(int col,int i){
int sz=slen[col];
while(sz>1&&slope(s[col][sz-2],s[col][sz-1])<slope(s[col][sz-1],i))
sz--,slen[col]--,s[col].db();
s[col].pb(i);
slen[col]++;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",a+i);
b[i]=++ecnt[a[i]];
}
for(int i=1;i<=n;i++){
insert(a[i],i);
int j=query(a[i],b[i]);
f[i]=(j?f[j-1]:f[i-1])+1LL*a[i]*(b[i]-b[j]+1)*(b[i]-b[j]+1);
}
printf("%lld\n",f[n]);
return 0;
}

【BZOJ4709】【Jsoi2011】柠檬的更多相关文章

  1. bzoj4709: [Jsoi2011]柠檬 斜率优化

    题目链接 bzoj4709: [Jsoi2011]柠檬 题解 斜率优化 设 \(f[i]\) 表示前 \(i\)个数分成若干段的最大总价值. 对于分成的每一段,左端点的数.右端点的数.选择的数一定是相 ...

  2. bzoj4709 [jsoi2011]柠檬

    Description Flute 很喜欢柠檬.它准备了一串用树枝串起来的贝壳,打算用一种魔法把贝壳变成柠檬.贝壳一共有 N (1 ≤ N  ≤ 100,000) 只,按顺序串在树枝上.为了方便,我们 ...

  3. BZOJ4709 Jsoi2011 柠檬【决策单调性+单调栈】

    Description Flute 很喜欢柠檬.它准备了一串用树枝串起来的贝壳,打算用一种魔法把贝壳变成柠檬.贝壳一共有 N (1 ≤ N ≤ 100,000) 只,按顺序串在树枝上.为了方便,我们从 ...

  4. BZOJ4709 JSOI2011柠檬(动态规划)

    首先要冷静下来发现这仅仅是在划分区间.显然若有相邻的数字相同应当划分在同一区间.还有一个显然的性质是区间的两端点应该相同且选择的就是端点的数.瞬间暴力dp就变成常数极小100002了.可以继续斜率优化 ...

  5. [BZOJ4709][JSOI2011]柠檬(斜率优化DP)

    显然选出的每一段首尾都是相同的,于是直接斜率优化,给每个颜色的数开一个单调栈即可. #include<cstdio> #include<vector> #include< ...

  6. [BZOJ4709][JSOI2011]柠檬 决策单调性优化dp

    题解: 解法1: 单调栈优化 首先发现一个性质就是 如果当前从i转移比从j转移更加优秀 那么之后就不会从j转移 所以我们考虑利用这个性质 我们要维护一个队列保证前一个超过后一个的时间单调不减 怎么来维 ...

  7. BZOJ4709: [Jsoi2011]柠檬(决策单调性)

    题意 题目链接 Sol 结论:每次选择的区间一定满足首位元素相同.. 仔细想想其实挺显然的,如果不相同可以删掉多着的元素,对答案的贡献是相同的 那么设\(f[i]\)表示到第\(i\)个位置的最大价值 ...

  8. 【BZOJ4709】[Jsoi2011]柠檬 斜率优化+单调栈

    [BZOJ4709][Jsoi2011]柠檬 Description Flute 很喜欢柠檬.它准备了一串用树枝串起来的贝壳,打算用一种魔法把贝壳变成柠檬.贝壳一共有 N (1 ≤ N ≤ 100,0 ...

  9. 4709: [Jsoi2011]柠檬

    4709: [Jsoi2011]柠檬 https://www.lydsy.com/JudgeOnline/problem.php?id=4709 分析: 决策单调性+栈+二分. 首先挖掘性质:每个段选 ...

  10. 【BZOJ4709】柠檬(动态规划,单调栈)

    [BZOJ4709]柠檬(动态规划,单调栈) 题面 BZOJ 题解 从左取和从右取没有区别,本质上就是要分段. 设\(f[i]\)表示前\(i\)个位置的最大值. 那么相当于我们枚举一个前面的位置\( ...

随机推荐

  1. Unity 自定义编辑器窗口 画线

    最近在学习状态机, 想自己实现一个可视化编辑器, 需要将多个状态之间用线条连接起来, 效果如下: 代码如下: Material m;Vector2 start;Vector2 end;Color co ...

  2. 配置独立于系统的PYTHON环境

    配置独立于系统的PYTHON环境 python 当前用户包 一种解决方案是在利用本机的python环境的基础上,将python的包安装在当前user的.local文件夹下 一共有两种方式来实现pip的 ...

  3. VGGnet——从TFrecords制作到网络训练

    作为一个小白中的小白,多折腾总是有好处的,看了入门书和往上一些教程,很多TF的教程都是从MNIST数据集入手教小白入TF的大门,都是直接import MNIST,然后直接构建网络,定义loss和opt ...

  4. Netty源码分析第8章(高性能工具类FastThreadLocal和Recycler)---->第3节: recycler的使用和创建

    Netty源码分析第八章: 高性能工具类FastThreadLocal和Recycler 第三节: recycler的使用和创建   这一小节开始学习recycler相关的知识, recycler是n ...

  5. Django_杂

    1.url带一些GET参数"url?a=1&b=2",通过QueryDict以及其urlencode()进行处理 from django.http.request impo ...

  6. 第38次Scrum会议(12/4)【欢迎来怼】

    一.小组信息 队名:欢迎来怼 小组成员 队长:田继平 成员:李圆圆,葛美义,王伟东,姜珊,邵朔,阚博文 小组照片 二.开会信息 时间:2017/12/4 17:50~18:20,总计30min. 地点 ...

  7. 实验3 --俄罗斯方块 with 20135335郝爽

    一.   实验内容 (一)敏捷开发与XP 内容:1.敏捷开发(Agile Development)是一种以人为核心.迭代.循序渐进的开发方法. 2.极限编程(eXtreme Programming,X ...

  8. 在Windows下制作静态库和动态库

    一:静态库的创建 VC++6.0中new一个的为win32 static library工程,之后有二个选项.根据需求选吧. 具体的类或者函数的添加过程和标准的工程一样,直接创建新的类或者添加新 的. ...

  9. 注册表:DWORD

    百度百科 DWORD全称Double Word,是指注册表的键值,每个word为2个字节的长度,DWORD 双字即为4个字节,每个字节是8位,共32位. 在键值项窗口空白处单击右键,选择“新建”菜单项 ...

  10. elasticsearch文档-字段的mapping

    mapping == Mapping是指定义如何将document映射到搜索引擎的过程,比如一个字段是否可以查询以及如何分词等,一个索引可以存储含有不同"mapping types" ...