[ARC131D] AtArcher 题解

题意

数轴上有一个箭靶以 \(0\) 为轴心左右对称,给定每个得分区域的范围和分值,要求射 \(N\) 支箭在靶上,且任意两支箭的距离不少于 \(D\),求最大得分。保证从中心向两侧分数不增。特别的,如果有一只箭射在了分界点上,以较大得分为准。

思路

结论一:最优方案中,每只箭必定射在整点上。

显然。

结论二:最优方案中,两只相邻箭之间的距离必定恰好为 \(D\)

因为分数的单调性,我们可以把更靠外的那支箭往中心压,肯定不劣。

我们定义一支箭为中心箭当且仅当以其为中心将箭靶分成左右两半,左边箭的数量和右边箭的数量相差不超过 \(1\)

结论三:最优方案中,最靠近中心的那支箭必定是中心箭。

若中心箭不最靠近中心,我们可以把箭较多的那一部分取一支箭射到另一边,由于分数的单调性,肯定不劣。

结论四:最优方案中,以 \(0\) 为对称轴翻折,\([0,D)\) 中必定有一支箭,且其为中心箭。

显然。

于是考虑枚举靠右的中心箭的位置,那么 \(<0\) 的部分有恰好 \(\lfloor \dfrac{N}{2}\rfloor\) 支箭,\(\ge 0\) 的部分有恰好 \(\lceil\dfrac{N}{2}\rceil\) 支箭。知道中心箭的位置后,我们可以推出剩下 \(N-1\) 支箭的位置,于是我们就得到了一个 \(O(ND)\) 的解法。

考虑优化,可以发现,当我们把左半边和右半边分开考虑,那么分数的每一个分界点最多被跨越一次,换句话说,分数的变化次数最多为 \(O(M)\) 次。于是考虑维护分界点与跨越事件,每次只更新被跨越的分界点,时间复杂度 \(O(N+D+M)\)

#include <iostream>
#include <vector>

using namespace std;
using LL = long long;

const int kM = 1e5 + 1, kD = 1e6 + 1;

int n, m, d;
LL r[kM], s[kM], ans, ns, sd[2][kD];
vector<int> l[kD];

void G(LL n, int o) {
  ns = 0;
  for (int i = 0, j = 0; i < n; ++i) {
    for (; j < m && 1LL * i * d > r[j + 1]; ++j) {
    }
    ns += s[j];
  }
  sd[o][0] = ns;
  for (int p = 1; p <= d; ++p) {
    for (int i : l[p - 1]) {
      if (r[i + 1] < p + (n - 1LL) * d) {
        ns += s[i + 1] - s[i];
      }
    }
    sd[o][p] = ns;
  }
}

int main() {
  ios_base::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m >> d;
  for (int i = 0; i <= m; ++i) {
    cin >> r[i];
  }
  for (int i = 0; i < m; ++i) {
    cin >> s[i];
    l[r[i + 1] % d].push_back(i);
  }
  G(n / 2, 0), G((n + 1) / 2, 1);
  for (int i = 0; i < d; ++i) {
    ans = max(ans, sd[1][i] + sd[0][d - i]);
  }
  cout << ans;
  return 0;
}
posted @ 2023-03-28 18:23  bykem  阅读(29)  评论(0)    收藏  举报