LibreOJ 2035 「SDOI2016」征途

首先推一下方差的式子。

vi 表示第 i 天走的长度,¯v=1mmi=1vivi 的平均数,v 表示方差。

v=1mmi=1(vi¯v)2=1m(mi=1v2i2¯vmi=1vi+m¯v2)=1m(mi=1v2i)2m(mi=1vi)2+m(1mmi=1vi)2=1m(mi=1v2i)2m(mi=1vi)2+1m(mi=1vi)2=1m(mi=1v2i)1m2(mi=1vi)2

于是有 vm2=mmi=1v2i(mi=1vi)2

后面一项与方案无关,也不方便列进转移方程,不如单独提出来。
Fi,k 表示前 i 段路,走了 k 天的答案。
有转移方程 Fi,k=min0j<i(Fj,k1+m(sumisumj)2)
其中 sumi=ij=1vj

此时把第二维滚掉,设 fi=Fi,k,gi=Fi,k1
方程变为 fi=min0j<i(gj+m(sumisumj)2)

对于 0k<j<i,假设决策 j 优于决策 k,有 gj+m(sumisumj)2gk+m(sumisumk)2gj+m(sum2i2sumisumj+sum2j)gk+m(sum2i2sumisumk+sum2k)gj2msumisumj+msum2jgk2msumisumk+msum2k(gj+msum2j)(gk+msum2k)2msumi(sumjsumk)(gj+msum2j)(gk+msum2k)sumjsumk2msumi

维护一只下凸包即可。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <cstdio>
const int N = 3e3;
int n,m;
int q[N + 5],head,tail;
long long sum[N + 5],f[N + 5],g[N + 5];
inline double slope(int x,int y)
{
return (double)(g[x] + sum[x] * sum[x] - g[y] - sum[y] * sum[y]) / (sum[x] - sum[y]);
}
int main()
{
scanf("%d%d",&n,&m);
for(register int i = 1;i <= n;++i)
scanf("%lld",sum + i),sum[i] += sum[i - 1],g[i] = sum[i] * sum[i];
for(register int i = 2;i <= m;++i)
{
q[head = tail = 1] = 0;
for(register int j = 1;j <= n;++j)
{
for(;head < tail && slope(q[head],q[head + 1]) <= 2 * sum[j];++head);
f[j] = g[q[head]] + (sum[j] - sum[q[head]]) * (sum[j] - sum[q[head]]);
for(;head < tail && slope(q[tail - 1],q[tail]) >= slope(q[tail],j);--tail);
q[++tail] = j;
}
for(register int j = 1;j <= n;++j)
g[j] = f[j];
}
printf("%lld\n",m * f[n] - sum[n] * sum[n]);
}

Related Issues not found

Please contact @Alpha1022 to initialize the comment