题解: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\) 个可移动的纪念碑。

\[lim=\left\lfloor\dfrac{n-m-\sum_{x\geq 1}(d_{+x}+d_{-x})}{2}\right\rfloor \]

那么显然会限制 \(\sum_{x\geq 1}r_x\leq lim\)

尝试刻画数轴上的移动,考虑把贡献拆到边上。对于一条连接 \(x-1,x\) 的边,设初状态中 \(\geq x\) 的位置共有 \(s\) 个纪念碑,末状态中共有 \(t\) 个纪念碑,则这条边的贡献为 \(|s-t|\),也就是

\[\left|\sum_{y\geq x}s_{+y}-\sum_{y\geq x}(d_{+y}+r_y)\right| \]

\(a_{+x}=\sum_{y\geq x}(s_{+y}-d_{+y})\)\(z_{x}=\sum_{y\geq x}r_y\),那么贡献可以写成 \(|a_{+x}-z_x|\)

同理定义 \(a_{-x}\),则最终总的移动距离就是

\[\sum_{x\geq 1}(|a_{+x}-z_x|+|a_{-x}-z_x|) \]

放到数轴上看,上式可以化成

\[\sum_{x\geq 1}|a_{+x}-a_{-x}|+2\sum_{x\geq 1}\operatorname{dist}(z_x,[\min(a_{+x},a_{-x}),\max(a_{+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]\)

现在问题转化成求

\[\min_{\substack{z_1\geq z_2\geq\cdots\\0\leq z_i\leq lim}}\sum_{x\geq 1}\operatorname{dist}(z_x,[L_x,R_x]) \]

这个是 slope trick 板子。令 \(f_{i,j}\) 表示考虑 \(z[i..V]\)\(z_i\leq j\) 时的最小总和。转移为

\[f_{i,j}=\min_{k\leq j}\{f_{i+1,k}+\operatorname{dist}(k,[L_i,R_i])\} \]

用大根堆维护拐点,枚举到 \(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;
}
posted @ 2026-08-12 16:51  P2441M  阅读(23)  评论(0)    收藏  举报