SDOI 2016 征途 (斜率优化)
题目描述:
Pine开始了从S地到T地的征途。
从S地到T地的路可以划分成
Pine计划用
Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小。
帮助Pine求出最小方差是多少。
设方差是
输入输出格式
输入格式:
第一行两个数
第二行
输出格式:
一个数,
题解
拿到题之后没有思路,于是先来推一波式子。
最原始的方差表达形式为:
整理得到:
二项式展开之后可得:
最后整理得到最终形式:
最后将
此时可以发现前一项是一个常数,扔掉不理他→_→
那么问题转化为如何求
很明显可以有很朴素的状态
然鹅这样的DP是
为了方便,我们设计
一顿操作后可以得到斜率式的形式:
然后套斜率优化的套路就可以惹。
另外需要注意初始化
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 3005
typedef long long ll;
using namespace std;
int N, M;
int head, tail;
int q[MAXN];
ll sum[MAXN];
ll f[MAXN], g[MAXN];
inline ll read_int()
{
ll ret = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') {ret = (ret << 1) + (ret << 3) + int(c - 48); c = getchar();}
return ret * f;
}
inline ll pow(ll x) {return x * x;}
inline double X(int i) {return sum[i];}
inline double Y(int j) {return g[j] + pow(sum[j]);}
inline double slope(int i, int j) {return (Y(i) - Y(j)) / (X(i) - X(j));}
void init()
{
N = read_int(), M = read_int();
for(int i = 1; i <= N; i++)
{
sum[i] = read_int();
sum[i] += sum[i - 1];
g[i] = pow(sum[i]);
}
}
void dp()
{
for(int l = 1; l < M; l++)
{
head = 1, tail = 1;
q[1] = l;
for(int i = l + 1; i <= N; i++)
{
while(head < tail && slope(q[head], q[head + 1]) < 2 * sum[i])
head++;
f[i] = g[q[head]] + (pow(sum[i] - sum[q[head]]));
while(head < tail && slope(q[tail], q[tail - 1]) > slope(q[tail], i))
tail--;
q[++tail] = i;
}
memcpy(g, f, sizeof(f));
}
printf("%lld\n", f[N] * M - pow(sum[N]));
}
int main()
{
init();
dp();
return 0;
}