[SDOI2016] 征途

link

题目大意

题意题目已经说得很清楚了,就不用我再赘述了。

思路

第一眼看到这个题,就感觉和[NOI2009] 诗人小G有点像,只是贡献计算方式有所不同。

那么就直接考虑DP。

\(f_{i,j}\) 表示 \(i\) 段路,走了 \(j\) 天的路程方差。

注意,这里的方差是相对 \(m\) 天计算的,也就是前 \(j\) 天,每天的路程与 \(m\) 天的平均路程之差的平方和,再除以一个 \(m\),又根据题目需要,再乘上一个 \(m^2\)

显然,可以得出状态转移方程:

\[f_{i,j}=min_{1\le k \le i-1}\{f_{k,j-1}+\frac{(s_i-s_k-\frac{s_n}{m})^2}{m}\cdot m^2\} \]

看到后面贡献那一部分,开始推式子。

\[\frac{(s_i-s_k-\frac{s_n}{m})^2}{m}\cdot m^2\\ =\frac{[m(s_i-s_k)-s_{n}]^2}{m}\\ =\frac{m^2(s_i-s_k)^2+s_n^2-2ms_n(s_i-s_k)}{m}\\ =m(s_i-s_k)^2-2s_n (s_i-s_k)+\frac{s_n^2}{m}\\ =ms_i^2+ms_k^2-2ms_is_k-2s_ns_i+2s_ns_k+\frac{s_n^2}{m} \]

前面五项都是整数,好处理,只有最后一项可能是小数。

记前五项之和为 \(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\) 为决策变量。

经过移项处理,我们可以得到:

\[ms_k^2+2s_ks_n+f_{k,j-1}=2ms_is_k-ms_i^2+2s_is_n+f_{i,j} \]

于是:

\[k=2ms_i\\ b=f_{i,j}-ms_i^2+2s_is_n\\ y=ms_k^2+f_{k,j-1}+2s_ks_n\\ x=s_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;
}
posted @ 2021-07-09 08:49  After-glow  阅读(64)  评论(1)    收藏  举报