[ APIO 2010 ] 特別行動隊
阿新 • • 發佈:2018-11-13
\(\\\)
Description
- \(N\le 10^6,–5 ≤ a ≤ –1,|b| ≤ 10^7,|c| ≤ 10^7,1 ≤ x_i ≤ 100\)
\(\\\)
Solution
首先統計出 \(s[n]=\sum_{i=1}^nx_i\) 。
設 \(f[i]\) 為前 \(i\) 個人可以達到的最高戰鬥力,易得轉移
\[ f[i]=f[j]+a(s[i]-s[j])^2+b(s[i]-s[j])+c \]
列出直線方程的形式
\[ f[j]+as[j]^2-bs[j]=f[i]-as[i]^2-bs[i]-c+2as[i]s[j] \]
容易發現抽象的點就是 \((s[j],f[j]+as[j]^2-bs[j])\)
\(\\\)
設 \(t[i]=as[i]^2-bs[i]\) 。
當一個狀態下 \(k\) 比 \(j\) 優,有
\[ f[k]+as[k]^2-bs[k]-2as[i]s[k]> f[j]+as[j]^2-bs[j]-2as[i]s[j] \]
整理,化簡得
\[ f[k]+t[k]-f[j]-t[j]>2as[i](s[k]-s[j]) \]
\[ \frac{f[k]+t[k]-f[j]-t[j]}{s[k]-s[j]}>2as[i] \]
注意直線的斜率 \(2as[i]\) 是負數,且不斷變小,可以用單調佇列維護了。
同樣因為直線的斜率為負數,所以我們需要維護上凸包。
\(\\\)
Code
#include<cmath> #include<cstdio> #include<cctype> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define gc getchar #define R register #define N 1000010 using namespace std; typedef long long ll; inline ll rd(){ ll x=0; bool f=0; char c=gc(); while(!isdigit(c)){if(c=='-')f=1;c=gc();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();} return f?-x:x; } ll n,m,a,b,c,s[N],t[N],g[N],q[N],hd,tl,f[N]; inline double calck(ll x,ll y){ return (double)(f[x]+t[x]-f[y]-t[y])/(double)(s[x]-s[y]); } inline ll w(ll x,ll y){ return f[x]+t[x]+g[y]-2*a*s[x]*s[y]; } int main(){ n=rd(); a=rd(); b=rd(); c=rd(); for(R ll i=1;i<=n;++i){ s[i]=s[i-1]+rd(); t[i]=a*s[i]*s[i]-b*s[i]; g[i]=a*s[i]*s[i]+b*s[i]+c; } q[hd=tl=1]=0; for(R int i=1;i<=n;++i){ while(hd<tl&&calck(q[hd],q[hd+1])>2.0*a*s[i]) ++hd; f[i]=w(q[hd],i); while(hd<tl&&calck(q[tl],q[tl-1])<calck(i,q[tl])) --tl; q[++tl]=i; } printf("%lld\n",f[n]); return 0; }