题意:就是给一个序列,让你分成很多段,每段按照题目的公式算出答案,要所有的答案加起来最小
思路一:(参考)http://www.cnblogs.com/lidaxin/p/5116577.html
斜率优化。
设f[i]表示前i个数分段后的最小值,则转移式为f[i]=min{f[j]+(sum[i]-sum[j])^2+M},1<=j<i
进一步得:f[i]=min{ (f[j]+sum[j]^2-2*sum[i]*sum[j])+(sum[i]^2+M) }
设y(j)=f[j]+sum[j]^2,a[i]=sum[i],x(j)=sum(j),则f[i]=min{y(j)-2*a[i]*x(j)}+sum[i]^2+M
则要求min p=y+2ax , 单调队列维护下凸包。
while(L<R && q[L+1].y-2*sum[i]*q[L+1].x <= q[L].y-2*sum[i]*q[L].x) L++; now.x = sum[i]; now.y = q[L].y-2*sum[i]*q[L].x+2*sum[i]*sum[i]+m; while(L<R && cross(q[R-1],q[R],now)<=0) R--; q[++R] = now;now.y = y[i] = f[i]+sum[j]^2 = min{ (f[j]+sum[j]^2-2*sum[i]*sum[j])+(sum[i]^2+M) } + sum[i]^2
答案就是 y[n]=q[R].y=f[n]+sum[n]^2 ==> q[R].y-sum[n]^2;
代码一:
#include <bits/stdc++.h> using namespace std; typedef long long ll; #define mem(a) memset(a,0,sizeof(a)) #define mp(x,y) make_pair(x,y) const int maxn = 5e5+10; const int INF = 0x3f3f3f3f; const ll INFLL = 0x3f3f3f3f3f3f3f3fLL; inline ll read(){ ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } ////////////////////////////////////////////////////////////////////////// int n,m,a; int sum[maxn]; struct node{ int x,y; }now,q[maxn]; int cross(node A,node B,node sum){ return (B.x-A.x)*(sum.y-A.y) - (sum.x-A.x)*(B.y-A.y); } int main(){ while(scanf("%d%d",&n,&m)==2){ mem(sum); for(int i=1; i<=n; i++){ scanf("%d",&a); sum[i] = sum[i-1]+a; } int L=0,R=0; for(int i=1; i<=n; i++){ while(L<R && q[L+1].y-2*sum[i]*q[L+1].x <= q[L].y-2*sum[i]*q[L].x) L++; now.x = sum[i]; now.y = q[L].y-2*sum[i]*q[L].x+2*sum[i]*sum[i]+m; while(L<R && cross(q[R-1],q[R],now)<=0) R--; q[++R] = now; } printf("%d\n",q[R].y-sum[n]*sum[n]); } return 0; }
思路二: 参考:http://www.cnblogs.com/kuangbin/archive/2012/08/26/2657650.html
kuangbin大神= = 非常详细
#include <bits/stdc++.h> using namespace std; typedef long long ll; #define mem(a) memset(a,0,sizeof(a)) #define mp(x,y) make_pair(x,y) const int maxn = 5e5+10; const int INF = 0x3f3f3f3f; const ll INFLL = 0x3f3f3f3f3f3f3f3fLL; inline ll read(){ ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } ////////////////////////////////////////////////////////////////////////// int n,m; int sum[maxn],f[maxn],q[maxn]; // int slope(int j,int k){ // WA!!! // return ((double)(f[j]+sum[j]*sum[j]-(f[k]+sum[k]*sum[k]))) / ((double)(2*(sum[j]-sum[k]))); // } int getUP(int j,int k) { return f[j]+sum[j]*sum[j]-(f[k]+sum[k]*sum[k]); } int getDOWN(int j,int k) { return 2*(sum[j]-sum[k]); } int main(){ //f[i]=min((sum[i]-sum[j])^2+m+f[j]) while(scanf("%d%d",&n,&m)==2){ sum[0],f[0]=0;q[0]=0; for(int i=1; i<=n; i++){ int a; scanf("%d",&a); sum[i] = sum[i-1]+a; } int L=0,R=0; for(int i=1; i<=n; i++){ while(L<R && getUP(q[L+1],q[L]) <= sum[i]*getDOWN(q[L+1],q[L])) L++; int j = q[L]; f[i] = f[j] + (sum[i]-sum[j])*(sum[i]-sum[j]) + m; while(L<R && (getUP(i,q[R])*getDOWN(q[R],q[R-1]) <= getUP(q[R],q[R-1])*getDOWN(i,q[R]))) R--; q[++R] = i; } // int L=0,R=0; // for(int i=1; i<=n; i++){ // while(L<R && slope(q[L+1],q[L]) <= sum[i]) L++; // int j = q[L]; // f[i] = f[j] + (sum[i]-sum[j])*(sum[i]-sum[j]) + m; // while(L<R && slope(i,q[R]) <= slope(q[R],q[R-1])) R--; // q[++R] = i; // } printf("%d\n",f[n]); } return 0; }