题解 P11303【[NOISG 2021 Finals] Pond】/ SS230121C【Cut】

题解 SS230121C【CUT】

题目描述

花花和啾啾是好朋友。花花要给啾啾的花园清除杂草。

啾啾的后花园可以抽象为一个数轴,数轴上有 \(n\) 个杂草,从左到右编号为 \(1\)\(n\)。第 \(i\) 个杂草距离第 \(i+1\) 个杂草 \(d_i\),初始花花在第 \(k\) 个杂草上。

初始所有杂草高度为 \(0\), 每经过 \(1\) 秒会增长 \(1\)。花花每秒能走 \(1\) 单位距离,清除杂草不需要时间,清除后不会继续生长。

因为清除下来的杂草太多不方便运输。花花想知道要清除所有杂草,杂草被清除时高度之和最小是多少。

\(1\leq k\leq n\leq 3\times 10^5,1\leq d_i\leq 10^6\)

solution

subtask 5

这个 subtask 的具体限制是 \(k\leq 2000,d_i\geq d_{i+1},\forall i\not \equiv 0\pmod {100}\)。这是个啥?

考虑区间 dp。\(f_{l,r,0/1}\) 表示已经干掉了 \([l,r]\) 的草,当然 \(k\in[l,r]\),然后现在在 \(l\) 还是 \(r\)。决策是向哪一边走,可以对费用提前计算,就是我走一秒是对答案加上“所有没死的草的棵树”这么多。也很好写。这就 \(O(nk)\) 了。

这个特殊性质很神秘动人。实际上,我们发现,向右走的时候,如果 \(i>k,d_i\geq d_{i+1}\) 且我们走到 \(i+1\) 了,是不是会有一种继续走到 \(i+2\) 的冲动,肯定继续走啊,因为现在折返再回来会更远,感性理解一下。于是我们就断言折返的地方一定满足 \(d_i<d_{i+1}\)。于是就 \(O(nk/100)\) 了。

正解

首先定义一个东西,势能 \(U\)(我觉得不应该叫势能,但是没别的名字)。当我们现在在 \(p\) 时,势能 \(U\) 就等于 \(\sum_iT(i)\),其中 \(T(i)\) 是对草定义的:假设当前时间是 \(t\),也即高度,如果草在 \(t\) 前死亡,那么 \(T(i)\) 是他的死亡时间;否则,\(T(i)=t+dist(p,i)\)。非常奇怪,但是有性质比较诱人:

  • 踩死一棵草,势能不变,以后不需要管这棵草的距离以及高度。

  • 接近一棵草,势能不变,正负抵消。

  • 远离一棵草,势能 \(+2\)

  • 如果我们走到了 \(1\)\(n\),那么可以得知 \(ans\leq U\)。或者更极端一点 \(ans=U\),当 \(U\) 是记我们所能得到的最小势能时。为了以后方便讨论,我们以 \(k\) 为中心断开,\([1,k]\) 翻转变为左部 \([1,n]\)\([k,n]\)变为右部 \([1,m]\)。这样,如果我们走到了左部终点或右部终点,那么向相反方向走一轮,最终草全死了,势能就是答案。

开始 dp。记 \(f_i\) 是左部,表示在左部 \(i\) 号点的最小势能;\(g_j\) 是右部,表示在右部 \(i\) 号点的最小势能。有转移。

\[f_i=\min_j\{g_j+2(m-j)(s1_i+s2_j)\} \]

\[g_j=\min_i\{f_i+2(n-i)(s1_i+s2_j)\} \]

\(s1,s2\) 是两边距离的前缀和。以第一条为例解释,我首先走到右部 \(j\) 踩死右部 \([1,j]\) 上的草,得到势能 \(g_j\),然后折返经过 \(k\) 到达左部 \(i\),这个过程中,右部 \([1,j]\) 已经死了;\([j+1,m]\) 被远离,势能一直 \(+2\);左部 \([i+1,n]\) 始终被接近,势能不变;\([1,i]\) 被逐步接近之后踩死,势能一直不变。所以整个势能的变化就很优美。

但是这个 dp 方程的转移怎么在互相转移,很丑啊,我们观察到系数都是一堆正数,而且 \(f,g\) 各自单调。有方法,考虑像 dijkstra 一样转移,按照大小顺序逐个转移。维护两个指针 \(p_1,p_2\) 表示目前 \(f[0,p_1)\)\(g[0,p_2)\) 都已经确定(注意 \(f_0=g_0\) 且容易计算确定;计算和确定是不同的)。每一次,计算即将产生的 \(f[p_1],g[p_2]\),如果 \(f[p_1]<g[p_2]\),我们就确定 \(f[p_2]\),按照大小顺序转移;否则确定 \(g[p_2]\)。在计算的时候只用已确定的值转移。

斜率优化。

code

#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define endl "\n"
#define debug(...) void(0)
#endif
typedef long long LL;
struct dot {
  LL x, y;
  dot operator-(dot b) const { return {x - b.x, y - b.y}; }
  friend LL  cross(dot a, dot b) { return a.x * b.y - a.y * b.x; }
};
struct convexHull {
  dot q[300010];
  int L = 1, R = 0;
  void addPoint(LL x, LL y) {
    dot t = {x, y};
    while (L < R && cross(t - q[R], q[R - 1] - q[R]) <= 0) --R;
    q[++R] = t;
  }
  LL query(LL k) {
    while (L < R && cross(q[L + 1] - q[L], {1, k}) >= 0) ++L;
    return q[L].y - k * q[L].x;
  }
};
int n, m, _1, _2;
LL s1[300010], s2[300010], f[300010], g[300010];
convexHull F, G;
LL dp() {
  for (int i = 1; i <= n; i++) s1[i] += s1[i - 1];
  for (int i = 1; i <= m; i++) s2[i] += s2[i - 1];
  for (int i = 1; i <= n; i++) f[0] += s1[i];
  for (int i = 1; i <= m; i++) f[0] += s2[i];
  g[0] = f[0];
  int p1 = 0, p2 = 0;
  F.addPoint(2 * p1, f[p1] + 2 * s1[p1] * (n - p1));
  G.addPoint(2 * p2, g[p2] + 2 * s2[p2] * (m - p2));
  for (int t = 1; t <= n + m; t++) {
    if (p1 < n && (p2 == m || G.query(s1[p1 + 1]) + 2 * s1[p1 + 1] * m <= F.query(s2[p2]) + 2 * s2[p2] * n)) {
      //    f[++p1] = 1e18;
      //    for (int j = 0; j <= p2; j++) f[p1] = min(f[p1], g[j] + (s1[p1] + s2[j]) * 2 * (m - j));
      ++p1;
      f[p1] = G.query(s1[p1]) + 2 * s1[p1] * m;
      F.addPoint(2 * p1, f[p1] + 2 * s1[p1] * (n - p1));
    } else {
      //    g[++p2] = 1e18;
      //    for (int j = 0; j <= p1; j++) g[p2] = min(g[p2], f[j] + (s1[j] + s2[p2]) * 2 * (n - j));
      ++p2;
      g[p2] = F.query(s2[p2]) + 2 * s2[p2] * n;
      G.addPoint(2 * p2, g[p2] + 2 * s2[p2] * (m - p2));
    }
  }
  return min(f[n], g[m]);
}
int main() {
#ifndef LOCAL
freopen("cut.in", "r", stdin);
freopen("cut.out", "w", stdout);
  cin.tie(nullptr)->sync_with_stdio(false);
#endif  
  cin >> _1 >> _2;
  n = _2 - 1, m = _1 - _2;
  for (int i = 1; i <= n; i++) cin >> s1[i];
  for (int i = 1; i <= m; i++) cin >> s2[i];
  reverse(s1 + 1, s1 + n + 1);
  cout << dp() << endl;
  return 0;
}
posted @ 2024-01-21 20:35  caijianhong  阅读(2)  评论(0编辑  收藏  举报