Luogu P5504 [JSOI2011]柠檬

题目
首先我们容易想到,每一个连续段两端的颜色一定相同。
预处理出\(sum_i\)表示\(1\sim i\)中颜色为\(a_i\)的贝壳的个数。
\(f_i\)为前\(i\)个贝壳的最大答案。
那么容易写出转移方程\(f_i=\max\limits_{j\in[1,i]\wedge a_i=a_j}(f_{j-1}+a_i(sum_i-sum_j+1)^2)\)
这个东西显然可以斜率优化,每种颜色维护一个凸包即可。
可以拆成这样
\(f_i=a_i(sum_i+1)^2+\max\limits_{j\in[1,i]\wedge a_i=a_j}((-2sum_i)(a_jsum_j)+(f_{j-1}+a_jsum_j^2-2a_jsum_j))\)

#include<bits/stdc++.h>
#define ll long long
#define ld long double
#define pub push_back
#define pob pop_back
#define t1 s[id][s[id].size()-1]
#define t2 s[id][s[id].size()-2]
using namespace std;
const int N=100007,M=10007;const ld eps=1e-8;
int a[N],sum[N],pos[N];vector<int>s[N];ll x[N],y[N],f[N];
int read(){int x=0,c=getchar();while(!isdigit(c))c=getchar();while(isdigit(c))x=x*10+c-48,c=getchar();return x;}
ld slope(int i,int j){return (ld)(y[j]-y[i])/(x[j]-x[i]);}
ll sqr(int x){return 1ll*x*x;}
int main()
{
    int n=read(),i,id;
    for(i=1;i<=n;++i) a[i]=read(),sum[i]=sum[pos[a[i]]]+1,pos[a[i]]=i;
    for(i=1;i<=n;++i) x[i]=1ll*sum[i]*a[i];
    for(i=1;i<=n;++i)
    {
	id=a[i],y[i]=f[i-1]+1ll*a[i]*sum[i]*(sum[i]-2);
	while(s[id].size()>=2&&slope(t2,t1)<=slope(t2,i)+eps) s[id].pob();
	s[id].pub(i);
	while(s[id].size()>=2&&slope(t2,t1)<=2.0*sum[i]+eps) s[id].pob();
	f[i]=f[t1-1]+a[i]*sqr(sum[i]-sum[t1]+1);
    }
    return !printf("%lld",f[n]);
}
posted @ 2019-10-29 21:33  Shiina_Mashiro  阅读(112)  评论(0)    收藏  举报