[Luogu P4072] [BZOJ 4518] [SDOI2016]征途

本文探讨了一种算法,用于帮助角色Pine规划从SSS地到TTT地的旅行路线,目标是最小化每天行走距离的方差。通过动态规划和斜率优化技巧,文章详细解释了如何求解这一问题,并提供了实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

洛谷传送门
BZOJ传送门

题目描述

Pine开始了从 S S S地到 T T T地的征途。

S S S地到 T T T地的路可以划分成 n n n段,相邻两段路的分界点设有休息站。

Pine计划用 m m m天到达 T T T地。除第 m m m天外,每一天晚上Pine都必须在休息站过夜。所以,一段路必须在同一天中走完。

Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小。

帮助Pine求出最小方差是多少。

设方差是 v v v,可以证明, v × m 2 v\times m^2 v×m2是一个整数。为了避免精度误差,输出结果时输出 v × m 2 v\times m^2 v×m2

输入输出格式

输入格式:

第一行两个数 n n n m m m

第二行 n n n 个数,表示 n n n 段路的长度

输出格式:

一个数,最小方差乘以 m 2 m^2 m2 后的值

输入输出样例

输入样例#1:
5 2
1 2 5 8 6
输出样例#1:
36

说明

对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 10 1 \le n \le 10 1n10

对于 60 % 60\% 60% 的数据, 1 ≤ n ≤ 100 1 \le n \le 100 1n100

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 3000 1 \le n \le 3000 1n3000

保证从 S S S T T T 的总路程不超过 30000 30000 30000

解题分析

我们先一波化简, 发现答案是 ( ∑ i = 1 n x ) 2 + n ∑ i = 1 n x 2 (\sum_{i=1}^{n} x)^2+n\sum_{i=1}^{n}x^2 (i=1nx)2+ni=1nx2。前面那玩意是个常数, 后面我们也只需要求出 ∑ i = 1 n x 2 \sum_{i=1}^{n}x^2 i=1nx2的最小值就好了。

d p [ i ] [ j ] dp[i][j] dp[i][j]表示我们已经走了 i i i天, 走了 j j j段路, s u m [ j ] sum[j] sum[j] j j j段路的长度和,那么设当前的最优解为第 i − 1 i-1 i1天走了 k k k段路 ,我们尝试从第 u u u段路 ( u > k ) (u>k) (u>k)去转移 ,如果更优的话那么就有:
d p [ i − 1 ] [ k ] + ( s u m [ j ] − s u m [ k ] ) 2 > d p [ i − 1 ] [ u ] + ( s u m [ j ] − s u m [ u ] ) 2 d p [ i − 1 ] [ k ] − 2 ∗ s u m [ j ] ∗ s u m [ k ] + s u m [ k ] 2 > d p [ i − 1 ] [ u ] − 2 ∗ s u m [ j ] ∗ s u m [ u ] + s u m [ u ] 2 dp[i-1][k]+(sum[j]-sum[k])^2>dp[i-1][u]+(sum[j]-sum[u])^2 \\dp[i-1][k]-2*sum[j]*sum[k]+sum[k]^2>dp[i-1][u]-2*sum[j]*sum[u]+sum[u]^2 dp[i1][k]+(sum[j]sum[k])2>dp[i1][u]+(sum[j]sum[u])2dp[i1][k]2sum[j]sum[k]+sum[k]2>dp[i1][u]2sum[j]sum[u]+sum[u]2
我们发现有一部分只与 j j j有关, 提出来:
d p [ i − 1 ] [ u ] − d p [ i − 1 ] [ k ] + s u m [ u ] 2 − s u m [ k ] 2 s u m [ u ] − s u m [ k ] &lt; 2 ∗ s u m [ j ] \frac{dp[i-1][u]-dp[i-1][k]+sum[u]^2-sum[k]^2}{sum[u]-sum[k]}&lt;2*sum[j] sum[u]sum[k]dp[i1][u]dp[i1][k]+sum[u]2sum[k]2<2sum[j]
发现右边左边都是单增的, 那么就可以一波斜率优化了。
听说可以带权二分搞到 O ( n l o g ( ∑ x ) ) O(nlog(\sum x)) O(nlog(x))?蒟蒻不会, 留坑…
代码如下:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define db double
#define MX 3050
#define ll long long
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    for (; !isdigit(c); c = gc);
    for (;  isdigit(c); c = gc)
    x = (x << 1) + (x << 3) + c - 48;
}
template <class T> IN T sqr (const T &x) {return x * x;}
ll dp[MX][MX], sum[MX];
int que[MX];
int tot, seg, head, tail;
IN db slope(R int cur, R int now, R int tar) {return 1.0 * (dp[cur][now] - dp[cur][tar] + sqr(sum[now]) - sqr(sum[tar])) / (sum[now] - sum[tar]);}
int main(void)
{
    int buf, best;
    in(tot), in(seg);
    std::memset(dp, 63, sizeof(dp)); dp[0][0] = 0;
    for (R int i = 1; i <= tot; ++i) in(buf), sum[i] = sum[i - 1] + buf;
    for (R int i = 1; i <= tot; ++i) dp[1][i] = sqr(sum[i]);
    for (R int i = 2; i <= seg; ++i)
    {
        head = 1, tail = 0;
        std::memset(que, 0, sizeof(que));
        for (R int j = 1; j <= tot; ++j)
        {
            W (head < tail && slope(i - 1, que[head], que[head + 1]) <= 2 * sum[j]) ++head;
            best = que[head];
            dp[i][j] = dp[i - 1][best] + sqr(sum[j] - sum[best]);
            W (head < tail && slope(i - 1, que[tail - 1], que[tail]) > slope(i - 1, que[tail], j)) --tail;
            que[++tail] = j;
        }
    }
    printf("%lld", 1ll * seg * dp[seg][tot] - sqr(sum[tot]));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值