[斜率優化dp] HDU 2829 Lawrence
阿新 • • 發佈:2020-07-25
題目大意
題解
\(g(L,R)=\sum_{i=L}^{R}\sum_{j=i+1}^{R}val[i]\times val[j]=\frac{1}{2}\left(\sum_{i=L}^{R}\sum_{j=L}^{R}val[i]\times val[j]-\sum_{i=L}^R val[i]^2\right)\)
\(=\frac{1}{2}\left(\left(\sum_{i=L}^R val[i]\right)^2-\sum_{i=L}^R val[i]^2\right)\)
記 \(s[n]=\sum_{i=1}^{n} val[i]\), \(s2[n]=\sum_{i=1}^n val[i]^2\)
則 \(g(L,R)=\frac{1}{2}\left(\left(s[R]-s[L-1]\right)^2-\left(s2[R]-s2[L-1]\right)\right)\)
設 \(dp[i][j]\) 表示前 \(i\) 個倉庫,炸燬 \(j\) 條鐵路的最小价值,
則有 \(dp[i][j]=\min\{dp[k][j-1]+g(k+1,i)\},k<i\)。
將 \(g(k+1,i)=\frac{1}{2}\left(\left(s[i]-s[k]\right)^2-\left(s2[i]-s2[k]\right)\right)\) 代入狀態轉移方程,
有 \(dp[i][j]=dp[k][j-1]+\frac{1}{2}\left(\left(s[i]-s[k]\right)^2-\left(s2[i]-s2[k]\right)\right)\)
整理可得,\(2dp[k][j-1]+s[k]^2+s2[k]=2s[i]s[k]+2dp[i][j]+s2[i]-s[i]^2\)。
不妨令 \(y=2dp[k][j-1]+s[k]^2+s2[k],K=2s[i],x=s[k]\),
則原式等於 \(y=Kx+2dp[i][j]+s2[i]-s[i]^2\),
發現斜率 \(K=2s[i]\) 單調遞增,顯然可以斜率優化,使用單調佇列維護一個下凸殼。
時間複雜度 \(O(nm)\) 。
Code
#include <iostream> #include <algorithm> #include <cstring> #include <string> #include <cstdio> #include <vector> using namespace std; #define RG register int #define LL long long template<typename elemType> inline void Read(elemType &T){ elemType X=0,w=0; char ch=0; while(!isdigit(ch)) {w|=ch=='-';ch=getchar();} while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); T=(w?-X:X); } LL Value[1005],dp[1005][1005]; LL s[1005],s2[1005]; int Q[1005]; int N,M,head,tail; inline LL y(int j,int k){return (dp[k][j-1]<<1)+s[k]*s[k]+s2[k];} inline LL x(int k){return s[k];} inline LL K(int i){return s[i]<<1;} inline LL g(int L,int R){return ((s[R]-s[L-1])*(s[R]-s[L-1])-(s2[R]-s2[L-1]))>>1;} inline void maintain(int i,int j,int k){dp[i][j]=dp[k][j-1]+g(k+1,i);} LL Solve(){ memset(dp,0x3f,sizeof(dp)); for(RG i=1;i<=N;++i) dp[i][0]=g(1,i); for(RG j=1;j<=M;++j){ dp[j][j]=0; head=1;tail=0; Q[++tail]=j; for(RG i=j+1;i<=N;++i){ while(tail-head+1>=2){ int a=Q[head],b=Q[head+1]; if(y(j,b)-y(j,a)<=K(i)*(x(b)-x(a))) ++head; else break; } maintain(i,j,Q[head]); while(tail-head+1>=2){ int a=Q[tail-1],b=Q[tail]; if((y(j,b)-y(j,a))*(x(i)-x(a))>=(y(j,i)-y(j,a))*(x(b)-x(a))) --tail; else break; } Q[++tail]=i; } } return dp[N][M]; } int main(){ while(~scanf("%d%d",&N,&M)){ if(N==0 || M==0) break; for(RG i=1;i<=N;++i){ Read(Value[i]); s[i]=s[i-1]+Value[i]; s2[i]=s2[i-1]+Value[i]*Value[i]; } printf("%lld\n",Solve()); } return 0; }