[SDOI2016]征途
阿新 • • 發佈:2018-04-12
show 前綴和 表示 lin .com 前綴 pre ans fin
因為\(mv=sum且\sum_{i=1}^mb_i=sum\)所以
\[ans=m\sum_{i=1}^mb_i^2-sum^2\]
由於\(sum和m\)都是已知的,所以我們要做的其實就是把這個序列分成\(m\)段,使得每一段的和的平方和最小。
然後可以\(DP\),設\(f[i][j]\)表示前\(i\)條路劃分為\(m\)段的最優解,也就是最小平方和。
然後對路徑統計前綴和,\(a[i]\)表示前\(i\)條路的總長。
顯然\(f[i][j]=max\{f[k][j-1]+(a[i]-a[k])^2\}(k<i)\)
這個轉移是\(O(n^3)\)的,顯然是跑不出來的。
然後我們會發現,這個可以用斜率優化啊。
若決策\(s_2優於s_1(s_1<s_2)\)
當且僅當\(f[s_1][j-1]+(a[i]-a[s_1])^2>=f[s_2][j-1]+(a[i]-a[s_2])^2\)
亂搞一波化簡就會得到
若有
\[a[i]>=\frac{f[s_1][j-1]+a[s_1]^2-f[s_2][j-1]-a[s_2]^2}{2(a[s_1]-a[s_2])}(s_1<s_2)\]
那麽決策\(s_2優於s_1\)
顯然\(a[i]\)是單調遞增的,所以我們用單調隊列維護一個單調遞增的斜率即可。不會的戳我
[SDOI2016]征途
題目
思路
這題目很繞(其實也還好)。
首先讓我們看看要求的到底是什麽。
\[ans=m^2\times s^2(s^2為方差)\]
定義\(v\)為每一段的平均數,\(b_i\)為第\(i\)段的長度,\(sum\)為路徑總長。
所以
\[s^2=\frac{1}{m}\sum_{i=1}^m(b_i-v)^2\]
\[ans=m\sum_{i=1}^m(b_i-v)^2\]
完全平方式展開
\[ans=m\sum_{i=1}^m(b_i^2-2b_iv+v^2)\]
拆掉\(\sum\)
\[ans=m(mv^2-2v\sum_{i=1}^mb_i+\sum_{i=1}^mb_i^2)\]
因為\(mv=sum且\sum_{i=1}^mb_i=sum\)所以
\[ans=m\sum_{i=1}^mb_i^2-sum^2\]
由於\(sum和m\)都是已知的,所以我們要做的其實就是把這個序列分成\(m\)段,使得每一段的和的平方和最小。
然後可以\(DP\),設\(f[i][j]\)表示前\(i\)條路劃分為\(m\)段的最優解,也就是最小平方和。
然後對路徑統計前綴和,\(a[i]\)表示前\(i\)條路的總長。
顯然\(f[i][j]=max\{f[k][j-1]+(a[i]-a[k])^2\}(k<i)\)
這個轉移是\(O(n^3)\)的,顯然是跑不出來的。
然後我們會發現,這個可以用斜率優化啊。
若決策\(s_2優於s_1(s_1<s_2)\)
當且僅當\(f[s_1][j-1]+(a[i]-a[s_1])^2>=f[s_2][j-1]+(a[i]-a[s_2])^2\)
亂搞一波化簡就會得到
若有
\[a[i]>=\frac{f[s_1][j-1]+a[s_1]^2-f[s_2][j-1]-a[s_2]^2}{2(a[s_1]-a[s_2])}(s_1<s_2)\]
那麽決策\(s_2優於s_1\)
顯然\(a[i]\)是單調遞增的,所以我們用單調隊列維護一個單調遞增的斜率即可。不會的戳我
完整代碼
#include<bits/stdc++.h> #define ll long long using namespace std; const int _=3e3+20; ll f[_][_],a[_]; int q[_]; double slope(int x,int y,int k){ return (double)((double)f[x][k]+a[x]*a[x]-f[y][k]-a[y]*a[y])/(a[x]-a[y])/2; } int main(){ int n,m; cin>>n>>m; for(int i=1;i<=n;++i){ scanf("%lld",&a[i]); a[i]+=a[i-1]; } for(int i=1;i<=n;++i){ f[i][1]=a[i]*a[i]; } for(int j=2;j<=m;++j){ int head=1,tail=1; q[1]=j-1; for(int i=j;i<=n;++i){ while(a[i]>=slope(q[head],q[head+1],j-1)&&head<tail)head++; int k=q[head]; f[i][j]=f[k][j-1]+(a[i]-a[k])*(a[i]-a[k]); while(slope(q[tail-1],q[tail],j-1)>=slope(q[tail],i,j-1)&&tail>head)tail--; q[++tail]=i; } } cout<<f[n][m]*m-a[n]*a[n]<<endl; return 0; }
[SDOI2016]征途