先算出加入 s1 后的双方气势差,再枚举第二次投放位置,比较加入 s2 后的差值绝对值。
OJ: luogu
题目 ID: P5016
难度:普及-
标签:模拟枚举
日期: 2026-06-19 01:07
题意
棋盘上有 n 个兵营,以第 m 个兵营为分界:
- 左边属于龙
- 右边属于虎
- 第
m个兵营不属于任何一方
一个兵营对某一方贡献的气势等于:
工兵数 × 到 m 的距离
先有一次固定事件:往 p1 号兵营加入 s1 位工兵。
然后你可以再选择一个兵营 p2,把 s2 位工兵全部放进去。
要求让最后龙方和虎方气势差的绝对值尽可能小,输出这个 p2。
思路
先看最直观的做法:
枚举每一个 p2,然后重新统计一遍加入 s1 和 s2 之后双方的总气势,比较差值绝对值。
这个办法很直接,也方便拿来对拍:
cpp
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 205;
int n, m, p1;
long long s1, s2;
long long c[MAXN];
long long calc_diff_after_choose(int p2) {
long long dragon = 0;
long long tiger = 0;
for (int i = 1; i <= n; i++) {
long long cnt = c[i];
if (i == p1) {
cnt += s1;
}
if (i == p2) {
cnt += s2;
}
if (i < m) {
dragon += cnt * (m - i);
}
else if (i > m) {
tiger += cnt * (i - m);
}
}
return dragon - tiger;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> c[i];
}
cin >> m >> p1 >> s1 >> s2;
int ans = 1;
long long best_abs = -1;
// 直接枚举最后把 s2 放到哪个兵营,并重新统计双方气势。
for (int p2 = 1; p2 <= n; p2++) {
long long diff = calc_diff_after_choose(p2);
long long cur_abs = llabs(diff);
if (best_abs == -1 || cur_abs < best_abs ||
(cur_abs == best_abs && p2 < ans)) {
best_abs = cur_abs;
ans = p2;
}
}
cout << ans << '\n';
return 0;
}但正式做法没必要每次都从头重算。
先把第一次加入 s1 之后的双方气势差记成:
diff = dragon - tiger
接下来枚举 p2 时:
- 如果
p2 < m,说明s2加到龙方,diff会增加s2 × (m - p2) - 如果
p2 > m,说明s2加到虎方,diff会减少s2 × (p2 - m) - 如果
p2 = m,双方气势都不变
所以每个位置的结果都能在
这样只要:
- 先算出第一次事件后的
diff - 枚举所有
p2 - 计算新的差值绝对值
- 取最小值;若相同取更小的兵营编号
就可以了。
注意:官方题面要求如果有多个最优答案,输出编号最小的兵营。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int n, m, p1, p2_ans;
long long s1, s2;
long long c[MAXN];
long long calc_initial_diff() {
long long dragon = 0;
long long tiger = 0;
for (int i = 1; i <= n; i++) {
if (i < m) {
dragon += c[i] * (m - i);
}
else if (i > m) {
tiger += c[i] * (i - m);
}
}
if (p1 < m) {
dragon += s1 * (m - p1);
}
else if (p1 > m) {
tiger += s1 * (p1 - m);
}
return dragon - tiger;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> c[i];
}
cin >> m >> p1 >> s1 >> s2;
long long diff = calc_initial_diff();
long long best_abs = -1;
p2_ans = 1;
for (int p2 = 1; p2 <= n; p2++) {
long long new_diff = diff;
if (p2 < m) {
new_diff += s2 * (m - p2);
}
else if (p2 > m) {
new_diff -= s2 * (p2 - m);
}
long long cur_abs = llabs(new_diff);
if (best_abs == -1 || cur_abs < best_abs ||
(cur_abs == best_abs && p2 < p2_ans)) {
best_abs = cur_abs;
p2_ans = p2;
}
}
cout << p2_ans << '\n';
return 0;
}复杂度
先统计一次原始气势,再枚举所有 p2。
时间复杂度是
总结
这题的关键是把“第二次投放后的总气势”看成对初始差值 diff 的一次简单修正。
一旦把这个关系写出来,枚举位置就够了。

