Loj#2035-[SDOI2016]征途【斜率优化】
正題
題目鏈接:https://loj.ac/problem/2035
題目大意
nnn個數字分成mmm段,要求方差最小。
解題思路
首先方差的公式∑i=1n(xi?∣x∣)2\sum_{i=1}^n(x_i-|x|)^2i=1∑n?(xi??∣x∣)2
其中∣x∣|x|∣x∣是不變的,定義w=∣x∣w=|x|w=∣x∣
設fi,jf_{i,j}fi,j?表示已經分到第iii段,到第jjj個時的最小方差和。
做前綴和si=∑j=1iais_i=\sum_{j=1}^ia_isi?=∑j=1i?ai?
之后有fk,i=min{fk?1,j+(si?sj)2+w2?2(si?sj)w}f_{k,i}=min\{f_{k-1,j}+(s_i-s_j)^2+w^2-2(s_i-s_j)w\}fk,i?=min{fk?1,j?+(si??sj?)2+w2?2(si??sj?)w}
去掉minminmin拆括號
fk,i=fk?1,j+si2?2sisj+sj2+w2?2siw+sjwf_{k,i}=f_{k-1,j}+s_i^2-2s_is_j+s_j^2+w^2-2s_iw+s_jwfk,i?=fk?1,j?+si2??2si?sj?+sj2?+w2?2si?w+sj?w
fk,i?si2+siw+2sisj?2sjw=fk?1,j+sj2f_{k,i}-s_i^2+s_iw+2s_is_j-2s_jw=f_{k-1,j}+s_j^2fk,i??si2?+si?w+2si?sj??2sj?w=fk?1,j?+sj2?
求fk,if_{k,i}fk,i?最小就是fk,i?si2+siwf_{k,i}-s_i^2+s_iwfk,i??si2?+si?w最小,后為了方便
定義F=fk,i?si2+siwF=f_{k,i}-s_i^2+s_iwF=fk,i??si2?+si?w
F+2(si?w)sj=fk?1,j+sj2F+2(s_i-w)s_j=f_{k-1,j}+s_j^2F+2(si??w)sj?=fk?1,j?+sj2?
然后有若干個決策點(sj,fk?1,j+sj2)(s_j,f_{k-1,j}+s_j^2)(sj?,fk?1,j?+sj2?)
每次有一條直線y=2(si?w)x+Fy=2(s_i-w)x+Fy=2(si??w)x+F經過某個決策點要求FFF最小
顯然因為si?ws_i-wsi??w的單調性和sjs_jsj?的單調性我們可以使用單調隊列維護一個下凸殼。
時間復雜度O(nm)O(nm)O(nm)
codecodecode
#include<cstdio> #include<cstring> #include<algorithm> #define pow2(x) ((x)*(x)) using namespace std; const int N=3100; struct node{double x,y;int num; }q[N]; int n,m; double s[N],f[N][N]; double slope(node x,node y) {return (y.y-x.y)/(y.x-x.x);} int main() {scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)scanf("%lf",&s[i]),s[i]=s[i]*m+s[i-1];double w=s[n]/m;for(int i=1;i<=n;i++)f[1][i]=pow2(s[i]-w);for(int k=2;k<=m;k++){int head=1,tail=1;q[1]=(node){s[k-1],f[k-1][k-1]+pow2(s[k-1]),k-1};for(int i=k;i<=n;i++){int z=2*(s[i]-w);while(head<tail&&slope(q[head],q[head+1])<z)head++;int p=q[head].num;f[k][i]=f[k-1][p]+pow2(s[i]-s[p]-w);node po=(node){s[i],f[k-1][i]+pow2(s[i]),i};while(head<tail&&slope(po,q[tail])<slope(q[tail-1],q[tail]))tail--;q[++tail]=po;}}printf("%.0lf",f[m][n]/m); }總結
以上是生活随笔為你收集整理的Loj#2035-[SDOI2016]征途【斜率优化】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 特长生考试相关
- 下一篇: 联想在用心做平板联想平板电脑