雅禮集訓 Day2 T1 施工
阿新 • • 發佈:2019-02-01
首先有一個比較好想的結論(誰說的。。。一點都不好想!)就是最優的情況一定是有兩端高於中間的一段平地。
因為一段本來有高度差的一起增高等於沒用,所以我們可以把最終高度相等的作為一段,無論這段有多少個。
而這樣做的條件是兩邊比中間高。
這樣我們得到一個dp式子,這個是個二次函式,可以直接求出t的最小值
就是理想高度,這樣就求出了最小的來更新,而對於j的選取,就是在i前面第一個比它高的為止,因為維護一個單調遞減的
序列,如果拿出的點比i小,那麼因為這個點前面的比這個點大(單調遞減),所以可以作為j和i作為一組,當這個點比i高時
就不合法了 ,用單調佇列可以優化掉正常要列舉的三維。
#include<cstdio> #include<cmath> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; int n; ll c; ll val[1000006]; ll ss[2][1000006]; int stak[1000006],top; ll dp[1000006]; ll gett(int x,int y,ll mx) { ll a=y-x-1; ll b=-2*(ss[0][y-1]-ss[0][x])-(x!=0)*1ll*c-(y!=n+1)*1ll*c; ll cc=ss[1][y-1]-ss[1][x]+(x!=0)*1ll*val[x]*c+(y!=n+1)*1ll*val[y]*c; ll t=-1*(ll)floor((double)b/((double)a*2.0)+0.5); t=max(t,mx); if(x!=0)t=min(t,val[x]); if(y!=n+1)t=min(t,val[y]); return a*t*t+b*t+cc; } int main() { freopen("construct.in","r",stdin); freopen("construct.out","w",stdout); scanf("%d%I64d",&n,&c); for(int i=1;i<=n;i++) { scanf("%I64d",&val[i]); ss[0][i]=ss[0][i-1]+val[i]; ss[1][i]=ss[1][i-1]+val[i]*val[i]; } val[0]=val[n+1]=0x3f3f3f3f; stak[top++]=0; for(int i=1;i<=n+1;i++) { //dp[i]=dp[i-1]+(i==1||i==n+1)?0:abs(val[i]-val[i-1])*c;有毒的三步運算子 if(i==1||i==n+1) { dp[i]=dp[i-1]; }else { dp[i]=dp[i-1]+abs(val[i]-val[i-1])*c; } while(top>0&&val[stak[top-1]]<=val[i]) { if(top>1) { dp[i]=min(dp[i],dp[stak[top-2]]+gett(stak[top-2],i,val[stak[top-1]])); } top--; } stak[top++]=i; } printf("%I64d",dp[n+1]); return 0; }