[BZOJ4518][SDOI2016]征途(斜率优化DP)
4518: [Sdoi2016]征途
Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 1861 Solved: 1034
[Submit][Status][Discuss]Description
Pine开始了从S地到T地的征途。从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站。Pine计划用m天到达T地。除第m天外,每一天晚上Pine都必须在休息站过夜。所以,一段路必须在同一天中走完。Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小。帮助Pine求出最小方差是多少。设方差是v,可以证明,v×m^2是一个整数。为了避免精度误差,输出结果时输出v×m^2。Input
第一行两个数 n、m。第二行 n 个数,表示 n 段路的长度Output
一个数,最小方差乘以 m^2 后的值
Sample Input
5 2
1 2 5 8 6Sample Output
36HINT
1≤n≤3000,保证从 S 到 T 的总路程不超过 30000
Source
[Submit][Status][Discuss]
怎么感觉SD这套卷子大神们可以1h内随手AK啊,联赛难度的题目。
这题求$vm^2$绝对不是为了避免精度误差!是出题人为了掩藏裸的斜率优化DP的借口!
化一下式子就好了,下一维由上一维的单调队列得出,滚动即可。注意f[0]也要放进队列,化式子要谨慎一点。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 typedef long long ll; 6 using namespace std; 7 8 const ll N=100100,inf=1e18; 9 ll n,m,st,ed,a[N],s[N],q[N],f[N],g[N],ans; 10 11 ll Y(ll x){ return f[x]+s[x]*s[x]; } 12 13 int main(){ 14 freopen("journey.in","r",stdin); 15 freopen("journey.out","w",stdout); 16 scanf("%lld%lld",&n,&m); ans=inf; 17 rep(i,1,n) scanf("%lld",&a[i]),s[i]=s[i-1]+a[i],f[i]=s[i]*s[i]; 18 rep(i,2,m){ 19 st=0; ed=0; 20 rep(j,0,n){ 21 while (st<ed && Y(q[st+1])-Y(q[st])<=2*s[j]*(s[q[st+1]]-s[q[st]])) st++; 22 g[j]=f[q[st]]+s[j]*s[j]-2*s[j]*s[q[st]]+s[q[st]]*s[q[st]]; 23 while (st<ed && (Y(q[ed])-Y(q[ed-1]))*(2*(s[j]-s[q[ed]]))>=(Y(j)-Y(q[ed]))*(2*(s[q[ed]]-s[q[ed-1]]))) ed--; 24 q[++ed]=j; 25 } 26 ans=min(ans,g[n]); rep(j,0,n) f[j]=g[j]; 27 } 28 printf("%lld\n",ans*m-s[n]*s[n]); 29 return 0; 30 }