P5504 [JSOI2011]柠檬
题解
考虑如果一段的首尾有不被选的话,那我们可以把这段不选的处于首尾的分裂开更优,于是对于同一个颜色我们可以做 $\text{dp}$ ,状态和转移显然,然后假设对于 $i$ 有两个决策点 $j,k$ ,如果 $j<k$ 并且 $F(j)>F(k)$ 的话,由于二次函数的增长速度可以得到 $j$ 之后一直比 $k$ 更优,所以我们维护一个单调栈,然后要注意入栈的时候要让栈中相邻两个元素之间超过或被超过的时间也要递增即可,效率: $O(nlogn)$
代码
#include <bits/stdc++.h> #define LL long long using namespace std; const int N=1e5+5; int n,t[N],a[N],s[N],b[N];LL f[N]; struct O{int l,r,p;};vector<int>g[N]; LL F(int j,int v){ return f[j-1]+1ll*a[j]*v*v; } int J(int x,int y){ int l=1,r=n,v=n+1; while(l<=r){ int mid=(l+r)>>1; if (F(x,mid-s[x]+1)>=F(y,mid-s[y]+1)) v=mid,r=mid-1; else l=mid+1; } return v; } int main(){ scanf("%d",&n); for (int i=1,j;i<=n;i++){ scanf("%d",&j),s[i]=++b[a[i]=j]; while(t[j]>=2 && J(g[j][t[j]-2],g[j][t[j]-1])<=J(g[j][t[j]-1],i)) g[j].pop_back(),t[j]--; g[j].push_back(i);t[j]++; while(t[j]>=2 && J(g[j][t[j]-2],g[j][t[j]-1])<=s[i]) g[j].pop_back(),t[j]--; f[i]=F(g[j][t[j]-1],s[i]-s[g[j][t[j]-1]]+1); } cout<<f[n]<<endl;return 0; }