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

 题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4709

我好弱啊QAQ,网上dalao们的题解根本看不懂啊,折腾了几个小时,有一点明白了。

首先要把朴素dp方程退出来。

①题目中说每次从序列的左右选一端取,但是如果你真的照着题目说的这样做我也不知道会怎么样。事实上很明显不管怎么取,最终答案都只跟划分出的是哪几个区间有关。所以不妨从左端开始取。

②如果取一个区间,区间第一个贝壳的大小和最后一个贝壳的大小不一样,那么很明显可以去掉第一个或最后一个贝壳,把他们加入另一个区间贡献答案,而这一次选取的区间本身答案不会变。于是我们每次取一段区间都可以贪心地来取,使得第一个贝壳和最后一个贝壳大小一定相同。

有了这两个准则方程很容易就出来了f[i]=max{f[j1]+a[i](s[i]s[j]+1)2}

其中s[i]表示直到第i个数a[i]出现的次数。

考虑这个式子中的单调性,可以发现s[i]是递增的也就是说(s[i]s[j]+1)2会增大,而且会增大地越来越快。这就说明如果之前有一个k<j满足k更优,则k会永远比j更优。

于是对于每一个a[i]可以用一个单调栈维护,当栈顶第二个元素比第一个元素更优时,弹出就行了,直到结束时,取栈顶元素作为决策。(不弹出)

但是这样还有一个问题,可以发现,在某些情况下,可能会出现第二个元素劣于第一个元素,但是却第三个元素优于第一个元素,怎么办呢?

对于任意的j1<j2<i1<i2,想一想可以发现如果j1超过i1的时间小于j2超过i1的时间,那么j1超过i2的时间也一定比j2超过i2的时间早。对于求某一个j超过k的时间,可以用二分来求。这个时候方法就出来了,在将i压入栈之前,我们先判断第二个元素超过i的时间是否小于第一个元素超过i的时间,如果是就弹栈,直到不满足条件,将i压入栈中。

这样做就满足了每一个元素超过上一个元素的时间也是单调的。

 

复制代码
 1 /**************************************************************
 2     Problem: 4709
 3     User: C20161009
 4     Language: C++
 5     Result: Accepted
 6     Time:444 ms
 7     Memory:3024 kb
 8 ****************************************************************/
 9  
10 #include<cstdio>
11 #include<cstring>
12 #include<algorithm>
13 #include<vector>
14 using namespace std;
15 typedef long long ll;
16 int inline readint(){
17     int Num;char ch;
18     while((ch=getchar())<'0'||ch>'9');Num=ch-'0';
19     while((ch=getchar())>='0'&&ch<='9') Num=Num*10+ch-'0';
20     return Num;
21 }
22 ll f[100010];
23 int n,a[100010];
24 int cnt[100010],s[100010];
25 vector <int> sta[10010];
26 ll inline cal(int x,int y){
27     return f[x-1]+(ll)a[x]*y*y;
28 }
29 int beyond(int x,int y){
30     int l=1,r=n,ret=n+1;
31     while(l<=r){
32         int mid=l+r>>1;
33         if(cal(x,mid-s[x]+1)>=cal(y,mid-s[y]+1)){
34             ret=mid;
35             r=mid-1;
36         }
37         else l=mid+1; 
38     }
39     return ret;
40 }
41 int main(){
42     n=readint();
43     for(int i=1;i<=n;i++){
44         int x=readint();
45         a[i]=x;
46         s[i]=++cnt[x];
47         while(sta[x].size()>=2&&beyond(sta[x][sta[x].size()-2],sta[x][sta[x].size()-1])<=beyond(sta[x][sta[x].size()-1],i)) sta[x].pop_back();
48         sta[x].push_back(i);
49         while(sta[x].size()>=2&&beyond(sta[x][sta[x].size()-2],sta[x][sta[x].size()-1])<=s[i]) sta[x].pop_back();
50         f[i]=cal(sta[x][sta[x].size()-1],s[i]-s[sta[x][sta[x].size()-1]]+1);
51     }
52     printf("%lld\n",f[n]);
53     return 0;
54 }
复制代码

 

posted @   halfrot  阅读(1659)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
点击右上角即可分享
微信分享提示