[JSOI2011] 柠檬
显然有暴力 dp。
然后需要注意到一个性质,就是 \(i\) 的最优决策点与 \(i\) 的颜色必定相同,且该区间一定会选端点的颜色,因为如果某一端和选的颜色不同,这一端都可以脱离出去而答案只会增大。
所以我们设 \(c[i]\) 表示 \(i\) 是同种颜色的第几个,那么转移就是 \(dp[i]=[col[j]=col[i]](dp[j-1]+(c[i]-c[j]+1)^2)\)。注意到决策点的颜色都一致,所以对每种颜色都建一个上凸包就好了。
然后注意同种颜色内斜率是单增的,所以其实凸包可以用单调栈维护,一旦凸包末尾的斜率小了就可以不管了,因为直线的斜率是单增的。时间复杂度 \(O(n)\)。
一个需要注意的细节是 \(dp[i]\) 得到后应该更新的是 \(col[i+1]\) 的凸包,不要找错了。。。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define cs const
#define in read()
inline int read(){
int p=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){p=p*10+c-48;c=getchar();}
return p*f;
}
cs int N=100005;
cs int M=10005;
int n,a[N],c[N],dp[N];
int cnt[M],fi[M];
struct node{
int v,nxt;
}e[N];
int hd[M],en;
inline void insert(int u,int v){
e[++en].v=v,e[en].nxt=hd[u],hd[u]=en;
}
inline int mpow(int x){return x*x;}
#define X(x) c[x+1]
#define Y(x) (dp[x]+a[x+1]*mpow(c[x+1]-1))
#define dx(x,y) (X(x)-X(y))
#define dy(x,y) (Y(x)-Y(y))
#define slope(x,y) ((double)dy(x,y)/dx(x,y))
signed main(){
n=in;
for(int i=1;i<=n;i++)a[i]=in,c[i]=++cnt[a[i]],fi[a[i]]=c[i]==1?i:fi[a[i]];
for(int i=1;i<=10000;i++)insert(i,0);
for(int i=1;i<=n;i++){
while(e[hd[a[i]]].nxt&&slope(e[hd[a[i]]].v,e[e[hd[a[i]]].nxt].v)<2*a[i]*c[i])hd[a[i]]=e[hd[a[i]]].nxt;
if(e[hd[a[i]]].v==0)dp[i]=dp[fi[a[i]]-1]+a[i]*mpow(c[i]);
else dp[i]=dp[e[hd[a[i]]].v]+a[i]*mpow(c[i]-c[e[hd[a[i]]].v+1]+1);
while(e[hd[a[i+1]]].nxt&&slope(e[hd[a[i+1]]].v,e[e[hd[a[i+1]]].nxt].v)<slope(i,e[hd[a[i+1]]].v))hd[a[i+1]]=e[hd[a[i+1]]].nxt;
insert(a[i+1],i);
}
cout<<dp[n];
return 0;
}