Luogu P5504 [JSOI2011] 柠檬
分析
发现每段头和尾一定相同
所以可以DP
令\(s[i]\)表示前\(i\)个与\(a[i]\)相同的个数,\(f[i]\)为答案
\[f[i]=f[j-1]+a[i]\times (s[i]-s[j]+1)^2
\]
有i,j,可以斜率优化,用\(Vector\)维护凸包
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+5,M=1e4+5;;
int n,a[N],s[N],lst[M];
ll f[N];
vector<int>V[M];
inline ll Y(int x) {
return f[x-1]+(ll)a[x]*s[x]*s[x]-2*a[x]*s[x];
}
inline int X(int x) {
return s[x];
}
inline void clear(vector<int> &x,int i) {
int t=x.size();
while(t>1&&(Y(x[t-1])-Y(x[t-2]))*(X(i)-X(x[t-1]))<=(Y(i)-Y(x[t-1]))*(X(x[t-1])-X(x[t-2]))) x.pop_back(),t--;
}
inline void del(vector<int> &x,int i) {
int t=x.size();
while(t>1&&(Y(x[t-1])-Y(x[t-2]))<=(ll)i*(X(x[t-1])-X(x[t-2]))) x.pop_back(),t--;
}
int main() {
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++) {
s[i]=s[lst[a[i]]]+1;
lst[a[i]]=i;
}
for(int i=1;i<=n;i++) {
clear(V[a[i]],i);
V[a[i]].push_back(i);
del(V[a[i]],2*a[i]*s[i]);
auto t=V[a[i]].end(); t--;
f[i]=f[(*t)-1]+(ll)a[i]*(s[i]-s[*t]+1)*(s[i]-s[*t]+1);
}
printf("%lld\n",f[n]);
return 0;
}