[SDOI2016] 征途
题目大意
题意题目已经说得很清楚了,就不用我再赘述了。
思路
第一眼看到这个题,就感觉和[NOI2009] 诗人小G有点像,只是贡献计算方式有所不同。
那么就直接考虑DP。
设 \(f_{i,j}\) 表示 \(i\) 段路,走了 \(j\) 天的路程方差。
注意,这里的方差是相对 \(m\) 天计算的,也就是前 \(j\) 天,每天的路程与 \(m\) 天的平均路程之差的平方和,再除以一个 \(m\),又根据题目需要,再乘上一个 \(m^2\) 。
显然,可以得出状态转移方程:
看到后面贡献那一部分,开始推式子。
前面五项都是整数,好处理,只有最后一项可能是小数。
记前五项之和为 \(val(i,k)\) 。
于是,
\(f_{i,j}=min_{1\le k \le i-1}\{f_{k,j-1}+val(i,k)+\frac{s_n^2}{m}\}\)
我们发现,每次计算 \(f_{j}\) 时,都是由 \(f_{j-1}\) 转移过来的,而每次这样的转移都要加上一个\(\frac{s_n^2}{m}\) 。
而我们要求的答案是 \(f_{n,m}\) ,此时已经加了 \(m\) 个 \(\frac{s_n^2}{m}\) ,就相当于直接加上了个 \(s_n^2\) 。
所以我们可以不用考虑每次转移的 \(+\frac{s_n^2}{m}\) ,直接在最后答案出 \(+s_n^2\) 。
方程就变成了:
\(f_{i,j}=min_{1\le k \le i-1}\{f_{k,j-1}+val(i,k)\}\)
时间复杂度:\(O(n^2m)\)
考虑优化。
这个方程的形式让我们直接想到单调队列优化,但因为在 \(val(i,k)\) 中,出现了乘积项 \(2ms_is_k\) ,所以考虑斜率优化。
斜率优化DP,将状态转移方程转换为 \(y=kx+b\) 的形式。
\(b\) 中仅包含与状态变量有关的项,所求状态包含在内。
\(kx\) 包含决策变量与状态变量的乘积项。
\(y\) 中仅包含与决策变量有关的项。
显然 \(i\) 为状态变量, \(k\) 为决策变量。
经过移项处理,我们可以得到:
于是:
斜率 \(k\) 单调递增,横坐标 \(x\) 单调递增,每次直接取队首为最优决策点。
最后答案为:\(f_{n,m}+s_n^2\)
时间复杂度:\(O(nm)\)
Code
注意要提前处理出 \(f_{x,1}\) 。
#include <bits/stdc++.h>
#define re register
#define int long long
#define ld long double
#define END system("pause")
#define inf 0x7fffffff
using namespace std;
inline int read()
{
re int x=0,f=1;
re char c=getchar();
while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+c-48;c=getchar();}
return x*f;
}
const int N=3e3+3;
int n,m;
int s[N];
int q[N],h,t;
int f[N][N];
inline int Y(int j,int k){return m*s[k]*s[k]+f[k][j-1]+2*s[k]*s[n];}
//inline int X(int k){return s[k];}
signed main()
{
n=read(),m=read();
for(re int i=1;i<=n;++i) s[i]=s[i-1]+read();
for(re int i=1;i<=n;++i) f[i][1]=s[i]*s[i]*m-2*s[i]*s[n];
for(re int j=2;j<=m;++j)
{
q[h=t=1]=j-1;
for(re int i=j;i<=n;++i)
{
while(h<t&&Y(j,q[h+1])-Y(j,q[h])<=2*m*s[i]*(s[q[h+1]]-s[q[h]])) ++h;
f[i][j]=f[q[h]][j-1]+m*s[q[h]]*s[q[h]]+m*s[i]*s[i]-2*m*s[i]*s[q[h]]-2*s[i]*s[n]+2*s[q[h]]*s[n];
//q[t-1],q[t],i
while(h<t&&(Y(j,q[t])-Y(j,q[t-1]))*(s[i]-s[q[t]])>=(Y(j,i)-Y(j,q[t]))*(s[q[t]]-s[q[t-1]])) --t;
q[++t]=i;
}
}
printf("%lld",f[n][m]+s[n]*s[n]);
//END;
return 0;
}

该文不被密码保护。
浙公网安备 33010602011771号