题意:n个数有代价ci n<=5e5,若将k个数连续写在一起的代价为:(sigma(c[i]))^2+M i=x+1~x+k,M为常数 求写出n个数的最小代价
s[i]为i的前缀和,设d[i] 写出前i个数的最小代价 d[i]=min(d[j]+(sum[i]-sum[j])^2+M) j=1~i j+1~i为最后一段长度
n<=5e5,直接做O(n^2)显然TLE,注意方程中sum[i]是递增的,利用斜率优化 时间复杂度降为O(n)
结论1:
考虑决策j,k 若决策j优于决策k d[j]+(sum[i]-sum[j])^2+M<d[k]+(sum[i]-sum[k])^2+M
化解得 (d[j]+sum[j]^2)-(d[k]+sum[k]^2)/(2*(sum[j]-sum[k])) < sum[i] 令yi=d[i]-sum[i]^2,xi=2*sum[i]
则:(yj-yk)/(xj-xk)<sum[i] 得决策j优于k当且仅当斜率g(j,k)<sum[i]
结论2:
k<j<i 若g(i,j)<g(j,k) 则j永远都不可能为最优决策 可以永久剔除
情况1:g(i,j)<sum[x] i优于j
情况2:g(i,j)>sum[x] j优于i 但是g(j,k)>g(i,j)>sum[x] k优于j.
则只剩下点满足g(i,j)>g(j,k) 从左到右斜率是递增的
可以用单调队列来维护可能的最优决策,队列元素a,b,c
新的点为d,若g(d,c)<g(c,b) 由上可知,队尾c出队 直到g(d,c')>=g(c',b') d加入末尾 ,队列中相邻两点的斜率是递增的.
对于某个状态i,若g(head+1,head)<sum[i] 则head+1优于head 又因为sum[i]是递增的 所以head可以出队(后面肯定不会用到)
直到g(head+1,head)>=sum[i] head优于head+1,由于单调队列递增 后面g(head+2,head+1)>=sum head+k优于head+k+1 k=0,1,2...
此时head为最优决策 执行DP(i,q[head])即可
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+20;
int n,M,c[N],s[N];
int dp[N];
int head,tail,q[N];
int DP(int i,int j)
{
return dp[j]+(s[i]-s[j])*(s[i]-s[j])+M;
}
int dy(int j,int k)//yj-yk
{
return (dp[j]+s[j]*s[j])-(dp[k]+s[k]*s[k]);
}
int dx(int j,int k)//xj-xk
{
return 2*(s[j]-s[k]);
}
int main()
{
while(cin>>n>>M)
{
s[0]=dp[0]=0;
for(int i=1;i<=n;i++)
scanf("%d",&c[i]),s[i]=s[i-1]+c[i];
head=tail=0;
q[tail++]=0;//决策:取一整段
//每个点最多进出队一次O(N)
for(int i=1;i<=n;i++)
{
//找到最优决策head,根据sum[i]递增&&g(h+1,h)<=sum[i] 淘汰head
while(head+1<tail&&dy(q[head+1],q[head])<=s[i]*dx(q[head+1],q[head]))
head++;
dp[i]=DP(i,q[head]);
//g(i,j)<g(j,k) 维护最优决策集合
while(head+1<tail && dy(i,q[tail-1])*dx(q[tail-1],q[tail-2])<=dy(q[tail-1],q[tail-2])*dx(i,q[tail-1]))
tail--;
q[tail++]=i;
}
cout<<dp[n]<<endl;
}
return 0;
}