题解:P17241 [IOI 2026] 纪念碑 / monuments
设 \(c_x\) 表示位置 \(x\) 上不可移动的纪念碑数量,\(s_x\) 表示可以移动的纪念碑数量。若 \(c_{+x}>c_{-x}\),则有 \(d_{-x}=c_{+x}-c_{-x}\) 个纪念碑需要被强制移动到 \(-x\) 处;同理,若 \(c_{+x}<c_{-x}\),则有 \(d_{+x}=c_{-x}-c_{+x}\) 个纪念碑需要被强制移动到 \(+x\) 处。
显然若 \(n-m<\sum_{x\geq 1}(d_{+x}+d_{-x})\) 则无解。
考虑最终 \(\pm x\) 位置上的纪念碑数量要是相同的,而我们已经有 \(c_{-x}+d_{-x}=c_{+x}+d_{+x}\),于是还可以往两边各放 \(r_x\) 个可移动的纪念碑。
设
那么显然会限制 \(\sum_{x\geq 1}r_x\leq lim\)。
尝试刻画数轴上的移动,考虑把贡献拆到边上。对于一条连接 \(x-1,x\) 的边,设初状态中 \(\geq x\) 的位置共有 \(s\) 个纪念碑,末状态中共有 \(t\) 个纪念碑,则这条边的贡献为 \(|s-t|\),也就是
设 \(a_{+x}=\sum_{y\geq x}(s_{+y}-d_{+y})\),\(z_{x}=\sum_{y\geq x}r_y\),那么贡献可以写成 \(|a_{+x}-z_x|\)。
同理定义 \(a_{-x}\),则最终总的移动距离就是
放到数轴上看,上式可以化成
前面的部分是定值,我们只需要最小化后面的部分。
考察 \(z\) 取值的限制,显然从外到内 \(z\) 的取值单调不减,且 \(0\leq z_x\leq lim\)。
设 \(L_x=\min(a_{+x},a_{-x})\),\(R_x=\max(a_{+x},a_{-x})\)。若 \([L_x,R_x]\) 与 \([0,lim]\) 有交则直接取交集;若 \(R_x<0\),给答案加上 \(-2R_x\),然后令 \(L_x=R_x=0\);若 \(L_x>lim\),给答案加上 \(2(L_x-lim)\),然后令 \(L_x=R_x=lim\)。这样我们就保证了 \([L_x,R_x]\subseteq [0,lim]\)。
现在问题转化成求
这个是 slope trick 板子。令 \(f_{i,j}\) 表示考虑 \(z[i..V]\),\(z_i\leq j\) 时的最小总和。转移为
用大根堆维护拐点,枚举到 \(i\) 时,先把 \(L_i\) 插入堆中,然后若 \(R_i\) 小于当前堆顶,把堆顶替换成 \(R_i\) 即可。
但是这题值域很大。不妨把所有非 \(0\) 的 \(|X_i|\) 拿出来排序并去重,得到 \(v_1,\cdots,v_k\)。容易观察到对于 \(x\in (v_{i-1},v_i]\),\(L_x,R_x\) 取值相同。因此把上述做法改成:把 \(w_i=v_i-v_{i-1}\) 个 \(L_i\) 插入堆中,再将堆顶至多 \(w_i\) 个 \(>R_i\) 的数替换成 \(R_i\) 即可。在堆中多维护一个 \(cnt\) 即可维护。
时间复杂度为 \(\mathcal{O}(n\log{n})\)。
代码很好写。
主要代码
ll get_cost(vector<int> X, vector<int> P) {
n = X.size();
m = P.size();
for (int i : P) vis[i] = true;
disc.reserve(n + 1);
disc = {0};
for (int i = 0; i < n; ++i) disc.emplace_back(abs(X[i]));
sort(disc.begin(), disc.end());
disc.erase(unique(disc.begin(), disc.end()), disc.end());
k = disc.size() - 1;
if (!k) return 0;
for (int i = 0; i < n; ++i) {
if (!X[i]) continue;
int v = lower_bound(disc.begin(), disc.end(), abs(X[i])) - disc.begin();
int p = X[i] >= 0 ? v + k : -v + k;
if (vis[i]) ++c[p];
else ++s[p];
}
int lim = n - m;
for (int i = 1; i <= k; ++i) {
d[-i + k] = max(c[i + k] - c[-i + k], 0);
d[i + k] = max(c[-i + k] - c[i + k], 0);
lim -= d[-i + k] + d[i + k];
}
if (lim < 0) return -1;
lim >>= 1;
ll ans = 0;
int ap = 0, an = 0;
priority_queue<pii> Q;
for (int i = k; i; --i) {
int r = disc[i] - disc[i - 1];
ap += s[i + k] - d[i + k];
an += s[-i + k] - d[-i + k];
auto [mn, mx] = minmax({ap, an});
ans += (ll)(mx - mn) * r;
int L, R;
if (mx < 0) {
ans += 2ll * -mx * r;
L = R = 0;
} else if (mn > lim) {
ans += 2ll * (mn - lim) * r;
L = R = lim;
} else {
L = max(mn, 0);
R = min(mx, lim);
}
int rst = r, cur = 0;
Q.emplace(L, r);
while (rst > 0 && Q.top().first > R) {
auto [val, cnt] = Q.top();
Q.pop();
int x = min(rst, cnt);
ans += 2ll * x * (val - R);
if (cnt > x) Q.emplace(val, cnt - x);
cur += x;
rst -= x;
}
if (cur) Q.emplace(R, cur);
}
return ans;
}

浙公网安备 33010602011771号