https://www.lydsy.com/JudgeOnline/problem.php?id=4709
https://www.luogu.com.cn/problem/P5504
这题跟之前的单调队列维护的斜率不太一样。。。直接维护一个单调栈
设dp[i]为前i个分段的最大值,那么转移到dp[i]的时候,必须选择a[i]作为s0,因为不选择a[i]的话,多出这个a[i]就没有意义。
那么我们就知道是对每种相同的颜色来考虑dp转移了
设sum[i]为a[i]在这个颜色出现的前缀和
得到式子:a[i]*(sum[k]-1)*(sum[k]-1)+dp[k-1]=2*a[i]*sum[i]*(sum[k]-1)+dp[i]-a[i]sum[i]*sum[i]
令2*a[i]*sum[i]为k,(sum[k-1])为x,前面的为y,且我们此时只对a[i]考虑,a[i]是个定值
那么x是递增的,y也是递增的,k也是递增的且大于0,我们希望dp[i]最大也就是截距最大,所以需要维护一个上凸包
所以我们维护一个单调栈而不是单调队列,因为上凸包末尾的斜率<当前的k的时候,我们需要弹出末尾的点,最大值也是在末尾取。
末尾斜率<当前的k直接用末尾两个点值比大小就可以了,无需再判断斜率。
#include<bits/stdc++.h>
using namespace std;
const int maxl=1e5+10;
typedef long long ll;
int n,mx,tot;
int a[maxl],b[maxl],sum[maxl],num[maxl];
int top[maxl];
ll dp[maxl];
struct node
{
ll x,y;
};
vector<node> s[maxl];
inline void prework()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
mx=max(mx,a[i]);
num[a[i]]++;
sum[i]=num[a[i]];
}
for(int i=1;i<=mx;i++)
s[i].resize(num[i]+1);
}
inline ll calc(ll k,node d)
{
return d.y-k*d.x;
}
inline bool cmpk(node a,node b,node c)
{
return 1.0*(b.y-a.y)*(c.x-b.x)<=1.0*(c.y-b.y)*(b.x-a.x);
}
inline void mainwork()
{
node d;ll k;
for(int i=1;i<=n;i++)
{
d.x=sum[i]-1;
d.y=1ll*a[i]*(sum[i]-1)*(sum[i]-1)+dp[i-1];
k=2ll*a[i]*sum[i];
while(top[a[i]]>1 && cmpk(s[a[i]][top[a[i]]-1],s[a[i]][top[a[i]]],d))
top[a[i]]--;
s[a[i]][++top[a[i]]]=d;
while(top[a[i]]>1 && calc(k,s[a[i]][top[a[i]]])<=calc(k,s[a[i]][top[a[i]]-1]))
top[a[i]]--;
dp[i]=calc(k,s[a[i]][top[a[i]]])+1ll*a[i]*sum[i]*sum[i];
}
}
inline void print()
{
printf("%lld",dp[n]);
}
int main()
{
prework();
mainwork();
print();
return 0;
}