[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;
}

浙公网安备 33010602011771号