斜率优化再整理
我是sb,这东西学了多久还不熟,故整理此文
当$dp$方程为$dp[i]=a[i]+b[j]$时,这个方程可以用单调队列从$O(n^2)$优化到$O(n)$
而当$dp$方程为$dp[i]=a[i]*b[j]+c[i]+d[j]$时,由于存在$a[i]*b[j]$这种既有$i$又有$j$的项,以上方法就不能用了,这就引入了斜率优化
对于本题,有$dp$方程:
$dp[i]=\min(dp[j]+(sum[i]+i-sum[j]-j-L-1)^2)(j<i)$
设$a[i]=sum[i]+i,b[i]=sum[i]+i+L+1$,则:
$dp[i]=dp[j]+(a[i]-b[j])^2=dp[j]+a[i]^2-2*a[i]*b[j]+b[j]^2$
移项得:
$2*a[i]*b[j]+dp[i]-a[i]^2=dp[j]+b[j]^2$
可以看作一条斜率为$2*a[i]$的直线,过点$(b[j],dp[j]+b[j]^2)$($b[j]$单增,$a[i]$单增)
$dp[i]$的含义即为:当直线过点$(b[j],dp[j]+b[j]^2)$时,在$y$轴的截距加上$a[i]^2$
下面就找斜率的最小值,显然当直线从下往上平移,在遇到第一个点时,此时的斜率是最小的
相当于维护一个凸包
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> #include<queue> #include<vector> #include<map> #include<set> #include<cstdlib> #include<ctime> using namespace std; #define LL long long #define ULL unsigned long long #define lowbit(x) ((x)&(-x)) #define For(x,i,j) for(int x=(i);x<=(j);x++) #define FOR(x,i,j) for(int x=(i);x>=(j);x--) #define ls(o) (o<<1) #define rs(o) (o<<1|1) #define debug(x) cout<<"debug : "<<x<<endl; inline int read(){ int x=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();} while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x*w; } const int N=1000005; LL n,a,b,c,x[N],s[N]; LL dp[N],q[N]; LL K(int i){ return 2*a*s[i]; } LL X(int i){ return s[i]; } LL Y(int i){ return dp[i]+a*s[i]*s[i]-b*s[i]; } double Slope(int i,int j){ return 1.0*(Y(j)-Y(i))/(X(j)-X(i)); } int main(){ //dp[j]+a*(sum[i]-sum[j])^2+b*(sum[i]-sum[j])+c cin>>n>>a>>b>>c; for(int i=1;i<=n;i++) cin>>x[i],s[i]=s[i-1]+x[i]; int head=0,tail=0; for(int i=1;i<=n;i++){ while(head<tail&&Slope(q[head],q[head+1])>K(i)) head++; //dp[i]=-K(i)*X(q[head])+Y(q[head])+a*s[i]*s[i]+b*s[i]+c; dp[i]=dp[q[head]]+a*(s[i]-s[q[head]])*(s[i]-s[q[head]])+b*(s[i]-s[q[head]])+c; while(head<tail&&Slope(q[tail-1],q[tail])<=Slope(q[tail],i)) tail--; q[++tail]=i; } cout<<dp[n]; return 0; }