BZOJ4709 浅谈单调栈
来源:互联网 发布:肌肉型小腿如何瘦 知乎 编辑:程序博客网 时间:2024/06/07 22:45
世界真的很大
单调栈这东西其实挺玄学的。。
看起来很好懂其实应用还是很广的,认真想起来还是很烧脑。
以题为例
description
Flute 很喜欢柠檬。它准备了一串用树枝串起来的贝壳,打算用一种魔法把贝壳变成柠檬。贝壳一共有 N (1 ≤ N ≤ 100,000) 只,按顺序串在树枝上。为了方便,我们从左到右给贝壳编号 1..N。每只贝壳的大小不一定相同,贝壳 i 的大小为 si(1 ≤ si ≤10,000)。变柠檬的魔法要求,Flute 每次从树枝一端取下一小段连续的贝壳,并选择一种贝壳的大小 s0。如果 这一小段贝壳中 大小为 s0 的贝壳有 t 只,那么魔法可以把这一小段贝壳变成 s0t^2 只柠檬。Flute 可以取任意多次贝壳,直到树枝上的贝壳被全部取完。各个小段中,Flute 选择的贝壳大小 s0 可以不同。而最终 Flute 得到的柠檬数,就是所有小段柠檬数的总和。Flute 想知道,它最多能用这一串贝壳变出多少柠檬。请你帮忙解决这个问题。
input
第 1 行:一个整数,表示 N。第 2 .. N + 1 行:每行一个整数,第 i + 1 行表示 si。
output
仅一个整数,表示 Flute 最多能得到的柠檬数。
sample input
52 2 5 2 3
sample out
21//Flute 先从左端取下 4 只贝壳,它们的大小为 2, 2, 5, 2。选择 s0 = 2,那么这一段里有 3 只大小为 s0 的贝壳,通过魔法可以得到 2×3^2 = 18 只柠檬。再从右端取下最后一只贝壳,通过魔法可以得到 1×3^1 = 3 只柠檬。总共可以得到 18 + 3 = 21 只柠檬。没有比这更优的方案了。
首先是大神题解预热
发现:题目告诉你可以从左边取或者从右边取,而事实上就等价于让你把原序列划分成几段
在最优解中,每一段的两个端点一定相同(否则的话把某一端的一个单分一段可以更优)
那么我们可以列出DP式,f[i]=max(f[j-1]+a*(sa[i]-sa[j]+1)^2),其中i和j的颜色都为a, sa为统计颜色为a的贝壳有多少个的前缀和
直接DP的话是n^2的
我们发现,对于四个颜色都为a的位置i < j < k < l,在计算f[l]的时候i和j的贡献相比于计算f[k]的时候都增加了,而i增加的一定比j多(因为函数y=x^2上凸)
同时我们发现,对于i和j,在我们知道了f[i]和f[j]之后就能O(log n)计算出i变得比j优的位置:
由于i < j,所以总有一个位置起,j的值会不如i优,且随着位置的不断后移,j的值永远也不会比i优了,因为二次函数的斜率是逐渐增加的,一旦在某个位置i算出来的最大值大于j算出的最大值,而对于之后的计算,i又
int byd(int x,int y){ int lf=1,rg=n,rid=n+1; while(lf<=rg) { int mid=(lf+rg)>>1; if(cal(x,mid-s[x]+1)>=cal(y,mid-s[y]+1)) { rid=mid; rg=mid-1; } else lf=mid+1; } return rid;}
那么我们对每种颜色维护一个单调栈,栈里是最有决策点,并关于前一个超过后一个的时间单调,每次求DP值的时候如果发现栈顶第二个元素比栈顶优了就弹栈:
维护的时候,首先在i入栈之前维护,看栈顶元素和栈顶-1的元素的最优决策点,栈顶的和当前的点的最优决策点的前后。
于是就O(n log n)解决了这个问题
完整代码:
#include<stdio.h>#include<vector>using namespace std;typedef long long dnt;int a[100010],c[10010],s[100010];int n,x;dnt f[100010];vector <int> st[10010];dnt cal(int x,int y){ return f[x-1]+ ( dnt ) a[x]*y*y;}int byd(int x,int y){ int lf=1,rg=n,rid=n+1; while(lf<=rg) { int mid=(lf+rg)>>1; if(cal(x,mid-s[x]+1)>=cal(y,mid-s[y]+1)) { rid=mid; rg=mid-1; } else lf=mid+1; } return rid;}bool check_h(int x,int i){ return byd(st[x][st[x].size()-2],st[x][st[x].size()-1])<=byd(st[x][st[x].size()-1],i);}bool check_t(int x,int i){ return byd(st[x][st[x].size()-2],st[x][st[x].size()-1])<=s[i];}int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&x); a[i]=x; s[i]=++c[x]; while(st[x].size()>=2&&check_h(x,i)) st[x].pop_back(); st[x].push_back(i); while(st[x].size()>=2&&check_t(x,i)) st[x].pop_back(); f[i]=cal(st[x][st[x].size()-1],s[i]-s[st[x][st[x].size()-1]]+1); } printf("%lld",f[n]);}
嗯,就是这样。
- BZOJ4709 浅谈单调栈
- [bzoj4709][JSOI2011]柠檬 单调栈斜率优化
- 浅谈单调栈思想
- bzoj4709柠檬
- BZOJ4709 [Jsoi2011]柠檬
- 浅谈单调队列优化dp
- 萌新浅谈单调队列
- 浅谈单调队列优化dp
- 浅谈单调队列的应用
- 单调栈 单调队列
- 单调栈
- 单调栈
- 单调栈
- 单调栈
- 单调栈
- 单调栈
- 单调栈
- 单调栈
- Gulp使用教程
- 学习SpringMVC——从HelloWorld开始
- ImageModeler v4.02 完整版+HTRI
- pwnable之simple login
- VS Code 列编辑功能说明
- BZOJ4709 浅谈单调栈
- Linux驱动USB驱动程序之概念介绍
- 形象理解计算机网络里的各种抽象概念
- JAVA编程练习
- rpm -e 惊魂夜
- 线程与线程池的实验与使用
- 机器学习与深度学习(四) 神经网络 (Neural Network) NN
- emmc FITRIM/discard命令在android系统中提升write performance的运用
- How Many Answers Are Wrong