[APIO2010] 特別行動隊 - 斜率優化dp
阿新 • • 發佈:2020-11-24
Description
給定長度為 \(n\) 的數列,將其分成若干份,每份的權值為 $ax^2 + bx + c $,其中 \(x\) 為該份的和。最大化權值總和。
Solution
設 \(f[i]\) 表示前 \(i\) 個數經過若干次劃分能得到的最大權值,則有
\[f[i] = \max_j f[j] + a(s[i]-s[j])^2 + b(s[i]-s[j]) + c \]\(b\) 項的總和是一個定值,先將它分出去,剩餘部分經過變形得到
\[f[j]+a\cdot s^2[j] = 2a \cdot s[i]s[j] + f[i]-a\cdot s^2 [i] -c \]以 \(s[j]\)
#include <bits/stdc++.h> using namespace std; #define int long long const int N = 1000005; int n,s[N],f[N],a,b,c; int q[N],head=0,tail=0; int getx(int j) { return s[j]; } int gety(int j) { return f[j]+a*s[j]*s[j]; } int getk(int i) { return 2*a*s[i]; } int getfi(int i,int val) { return val+a*s[i]*s[i]+c; } double slope(int i,int j) { return 1.0*(gety(i)-gety(j))/(getx(i)-getx(j)); } signed main() { ios::sync_with_stdio(false); cin>>n; cin>>a>>b>>c; int ans=0; for(int i=1;i<=n;i++) cin>>s[i]; for(int i=1;i<=n;i++) s[i]+=s[i-1]; for(int i=1;i<=n;i++) { while(head<tail && slope(q[head],q[head+1])>getk(i)) ++head; int j=q[head]; f[i]=getfi(i, gety(j)-getk(i)*getx(j)); while(head<tail && slope(i,q[tail])>slope(q[tail-1],q[tail])) --tail; q[++tail]=i; } cout<<f[n]+b*s[n]<<endl; }