其他分享
首页 > 其他分享> > P5504 柠檬题解

P5504 柠檬题解

作者:互联网

P5504 柠檬

考虑 \(dp\)

我们设 \(f_i\) 表示已经取下了前 \(i\) 个贝壳所得到的最大柠檬数

显然我们可以得到一个结论:每一段左右大小必然相等

因为若是左右两个端点不相等的话,必然有一个端点因为大小不同而没有贡献

这个端点就可以并到其他区建立得到更优解

我们用 \(c_i\) 表示点 \(i\) 的大小,\(poc_i\) 表示点 \(i\) 是大小相同的贝壳中的第几个

我们于是就可以写出 \(dp\) 方程式

\[f_i=\max\{f_{j-1}+c_i\cdot (poc_i-poc_j+1)\}(1\le j<i) \]

那么考虑把 \(\max\) 去掉并把式子展开,就能得到

\[f_i=f_{j-1}+c_i\cdot poc_i^2+c_j\cdot poc_j^2\cdot c_i-2\cdot c_i\cdot poc_i\cdot poc_j+2\cdot c_i\cdot poc_i-2c_j\cdot poc_j \\ f_{j}+c_j\cdot poc_j^2-2\cdot c_j\cdot poc_j=2\cdot c_i\cdot poc_i\cdot poc_j-c_i\cdot poc_i^2-2\cdot c_i\cdot poc_i+f_{i} \]

这有 \(i,j\) 的乘积项,按照套路要搞斜率优化 \(dp\)

\(\text X_i=pos_i\)

\(\text Y_i=f_{j}+c_j\cdot poc_j^2-2\cdot c_j\cdot poc_j\)

\(\text K_i=2\cdot c_i\cdot poc_io\)

由于这道题要求最大值,且 \(\text X\),\(\text K\) 单增,因此我们需要维护一个上凸包

额,这貌似不能单调队列维护,我们用单调栈

code

#include<bits/stdc++.h>
using namespace std;

#define ll long long

const int N=1e5+5;

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}

int n;
ll c[N],cnt[N],pos[N];
ll f[N];
vector <int> st[N];
#define t0 st[t].size()
#define t1 st[t][t0-1]
#define t2 st[t][t0-2]
inline double X(int i){return (double)pos[i];}
inline double Y(int i){return (double)f[i-1]+c[i]*pos[i]*pos[i]-2.0*c[i]*pos[i];}
inline double K(int i,int j){return ((Y(i)-Y(j))/(X(i)-X(j)));}
inline ll cal(int i,int j){
	return f[j-1]+c[i]*(pos[i]-pos[j]+1)*(pos[i]-pos[j]+1);
}

signed main(){
	n=read();
	for(int i=1;i<=n;++i){
		c[i]=read();
		pos[i]=++cnt[c[i]];
	}
	for(int i=1;i<=n;++i){
		int t=c[i];
		while(t0>=2&&K(t2,i)>=K(t2,t1)) st[t].pop_back();
		st[t].push_back(i);
		while(t0>=2&&cal(i,t1)<=cal(i,t2)) st[t].pop_back();
		f[i]=cal(i,t1);
	}
	printf("%lld\n",f[n]);
}

标签:cdot,题解,P5504,pos,柠檬,st,int,text,poc
来源: https://www.cnblogs.com/into-qwq/p/16533199.html