1. 程式人生 > >洛谷P3628 [APIO2010]特別行動隊(斜率優化)

洛谷P3628 [APIO2010]特別行動隊(斜率優化)

new open 同類項 freopen -- www. pro ios check

傳送門

先寫出轉移方程$$dp[i]=max\{dp[j]+a*(sum[i]-sum[j])^2+b*(sum[i]-sum[j])+c\}$$

假設$j$比$k$更優,則有$$dp[j]+a*(sum[i]-sum[j])^2+b*(sum[i]-sum[j])+c>dp[k]+a*(sum[i]-sum[k])^2+b*(sum[i]-sum[k])+c$$

展開,並消去同類項之後得$$dp[j]-2*a*sum[i]*sum[j]+a*sum[j]^2-b*sum[j]>dp[k]-2*a*sum[i]*sum[k]+a*sum[k]^2-b*sum[k]$$

移項,得$$(dp[j]+a*sum[j]^2-b*sum[j])-(dp[k]+a*sum[k]^2-b*sum[k])>2*a*sum[i]*sum[j]-2*a*sum[i]*sum[k]$$

設$Y[i]=dp[i]+a*sum[i]^2-b*sum[i],X[i]=sum[i]$

則有$$Y[j]-Y[k]>2*a*sum[i]*X[j]-2*a*sum[i]*X[k]$$

$$\frac{Y[j]-Y[k]}{X[j]-X[k]}>2*a*sum[i]$$

那麽就是要我們維護一個上凸包,簡單來說就是把原來維護下凸包的那些東西給反過來就好了(ps:我今天剛知道原來凸包還能是上凸的……我太菜了……)

 1 //minamoto
 2 #include<iostream>
 3 #include<cstdio>
 4
#define ll long long 5 using namespace std; 6 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 7 char buf[1<<21],*p1=buf,*p2=buf; 8 inline int read(){ 9 #define num ch-‘0‘ 10 char ch;bool flag=0;int res; 11 while(!isdigit(ch=getc()))
12 (ch==-)&&(flag=true); 13 for(res=num;isdigit(ch=getc());res=res*10+num); 14 (flag)&&(res=-res); 15 #undef num 16 return res; 17 } 18 const int N=1000005; 19 int sum[N],q[N],h,t,n;ll dp[N],a,b,c; 20 inline ll Y(int i){return dp[i]+a*sum[i]*sum[i]-b*sum[i];} 21 inline double slope(int j,int k){return 1.0*(Y(j)-Y(k))/(sum[j]-sum[k]);} 22 inline ll check(int x){return a*x*x+b*x+c;} 23 int main(){ 24 //freopen("testdata.in","r",stdin); 25 n=read(),a=read(),b=read(),c=read(); 26 for(int i=1;i<=n;++i) sum[i]=read()+sum[i-1]; 27 for(int i=1;i<=n;++i){ 28 int k=2*a*sum[i]; 29 while(h<t&&slope(q[h],q[h+1])>k) ++h; 30 dp[i]=dp[q[h]]+check(sum[i]-sum[q[h]]); 31 while(h<t&&slope(q[t],q[t-1])<slope(q[t-1],i)) --t;q[++t]=i; 32 } 33 printf("%lld\n",dp[n]); 34 return 0; 35 }

洛谷P3628 [APIO2010]特別行動隊(斜率優化)