标签:
题意: 给一段数列,要求分成若干段,有一个公式可算每段的花费,求整段数列最小花费。
题意很裸,不难得出状态转移方程:dp[i]=min{dp[j]+(sum[i]-sum[j-1]+i-j-l)^2}(j<i);
但是数据范围很大,o(n^2)过不了,于是就用斜率优化变成n的,就过了。
下面详细讲一下斜率优化:
斜率优化就是维护一个凸壳,使其严格上凸或下凸,具体情况具体分析,就本题而言设有k<j<i,且i从k转移来的,
则:dp[k]+(sum[i]-sum[k-1]+i-k-l)^2<=dp[j]+(sum[i]-sum[j-1]+i-j-l)^2,方程变形得到不等式:
dp[k]+(sum[k]+k+1+l)^2-dp[j]-(sum[j]+j+1+l)^2/(2*(sum[k]+k-sum[j]-j))<=sum[i]+i;
观察,这个式子左边是一个与k,j有关的斜率表达式,而右边只含有i,所以可用一个单调队列来维护,就降到了n的复杂度。
下面是代码:
1 /************************************************************** 2 Problem: 1010 3 User: 201484348 4 Language: C++ 5 Result: Accepted 6 Time:120 ms 7 Memory:5492 kb 8 ****************************************************************/ 9 10 #include<cstdio> 11 #include<iostream> 12 using namespace std; 13 typedef long long ll; 14 struct data{ 15 ll x,y; 16 }cmp,D[50010]; 17 int L,R,N,W; 18 ll C[500010]; 19 inline ll jud(data a,data b,data c) 20 { 21 return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x); 22 } 23 int main() 24 { 25 scanf("%d%d",&N,&W); 26 for (int i=1;i<=N;i++) 27 { 28 scanf("%lld",&C[i]); 29 C[i]+=C[i-1]; 30 } 31 for (int i=1;i<=N;i++) 32 { 33 while (L<R&&D[L].y-2*(i+C[i]-W-1)*D[L].x>=D[L+1].y-2*(i+C[i]-W-1)*D[L+1].x) L++; 34 cmp.x=i+C[i];cmp.y=D[L].y-2*(i+C[i]-W-1)*D[L].x+(i+C[i]-W-1)*(i+C[i]-W-1)+(i+C[i])*(i+C[i]); 35 while (L<R&&jud(D[R-1],D[R],cmp)<=0) R--; 36 D[++R]=cmp; 37 } 38 printf("%lld\n",D[R].y-(N+C[N])*(N+C[N])); 39 return 0; 40 }
标签:
原文地址:http://www.cnblogs.com/nieyunhe/p/5061120.html