题解:P15762 [JAG 2025 Summer Camp #1] Inside Yamanote

· · 题解

下文所有变量的角标都对 n 取模,如 i + 1 代表 (i + 1) \bmod n。

设首都为 pos,每个点到首都的距离为 d_i,若满足 |d_{i + 1} - d_i| \le l_i 且 d_i \ge 0 且存在 d_i = 0,则必然存在一种合法的建边方案。后两个条件分别代表距离非负、存在首都;第一个条件是因为可以让 pos 向 i(i \ne pos) 连边,边权为 d_i,当且仅当满足 |d_{i + 1} - d_i| \le l_i 时,pos, i, i + 1 在几何平面上构成三角形(或三点共线),则 pos 到 i 的距离为 d_i。

最后要求的声誉,是所有 d_{X_i} - d_{Y_i} 的总和。考虑每个 d_i 的贡献,必然是一个系数乘上 d_i,设其为 s_i。那么每次让 s_{X_i} 加一,s_{Y_i} 减一即可。由于每次都加一个、减一个,必然有 \sum\limits_{i = 0}^{n - 1} s_i = 0。

则答案为:\sum\limits_{i = 0}^{n - 1} s_i \times d_i。

然后就是人类智慧了。发现对于一种只满足 |d_{i + 1} - d_i| \le l_i 的 d_i 方案,必然可以令其变成合法方案(即变得同时满足之前所说的三个条件)。因为令 c = -\min d_i,令全体 d_i 加上 c,则此时最小的 d_i 变成 0,其余 d_i 同样 \ge 0,同时仍然满足 |d_{i + 1} - d_i| \le l_i(因为都加上同一个值,差还是不变)。

于是可以抛开首都,d_i 方案只需要满足 |d_{i + 1} - d_i| \le l_i,求 \sum\limits_{i = 0}^{n - 1} s_i \times d_i 的最大值。

推式子: $$ \begin{aligned} &\sum\limits_{i = 0}^{n - 1} s_i \times d_i\\ =& s_0 \times d_0 + \sum\limits_{i = 1}^{n - 1} s_i \times (d_0 + \sum\limits_{j = 0}^{i - 1} w_j)\\ =& d_0 \times \sum\limits_{i = 0}^{n - 1} s_i + \sum\limits_{i = 1}^{n - 1} s_i \times \sum\limits_{j = 0}^{i - 1} w_j\\ =& \sum\limits_{j = 0}^{n - 1} w_j \times \sum\limits_{i = j + 1}^{n - 1} s_i \end{aligned} $$ 设 $a_j = \sum\limits_{i = j + 1}^{n - 1} s_i$,$a_i$ 是不变的。则答案为 $\sum\limits_{i = 0}^{n - 1} w_i \times a_i$。 因此,问题转化为:求满足 $-l_i \le w_i \le l_i$ 且 $\sum\limits_{i = 0}^{n - 1} w_i = 0$ 的 $w_i$ 方案中,$\sum\limits_{i = 0}^{n - 1} w_i \times a_i$ 的最大值。 先抛开 $\sum\limits_{i = 0}^{n - 1} w_i = 0$ 的限制。由于 $a_i$ 有正有负,肯定是贪心地令 $a_i < 0$ 的 $w_i = -l_i,a_i \ge 0$ 的 $w_i = l_i$,设该答案为 $A$。 然后考虑这个限制。设 $neg$ 表示 $a_i < 0$ 的 $-w_i$ 之和,$pos$ 表示 $a_i \ge 0$ 的 $w_i$ 之和。 若 $a_i < 0$ 的 $w_i$ 加一,会使答案减少 $-a_i$,该 $w_i$ 可以从 $-l_i$ 加到 $l_i$,最多加 $2 \times l_i$ 次。 若 $a_i \ge 0$ 的 $w_i$ 减一,会使答案减少 $a_i$,该 $w_i$ 可以从 $l_i$ 减到 $-l_i$,最多减 $2 \times l_i$ 次。 如果 $neg > pos$,希望选择一些 $a_i < 0$ 的 $w_i$ 增加一些值,增加的总次数为 $k = neg - pos$。 等价于有 $2 \times l_i$ 个 $-a_i(a_i < 0)$,前 $k$ 小的数之和为 $B$,则答案为 $A - B$。 如果 $neg \le pos$,希望选择一些 $a_i \ge 0$ 的 $w_i$ 减少一些值,减少的总次数为 $k = pos - neg$。 等价于有 $2 \times l_i$ 个 $a_i(a_i \ge 0)$,前 $k$ 小的数之和为 $B$,则答案为 $A - B$。 开权值线段树,以 $a_i$ 为下标(或者 $-a_i$)。线段树上二分求前 $k$ 小之和即可。注意 $0 \le a_i \le m$,线段树开到 $m$ 不是 $n$。 ```cpp const int N = 200005; int n, m, q; int l[N], s[N], a[N]; struct segment_tree{ int sum[N << 2], cnt[N << 2]; #define ls x << 1 #define rs x << 1 | 1 void add(int x, int l, int r, int t, int c, int val){ if(l == r){ sum[x] += c * val; cnt[x] += c; return ; } int mid = (l + r) >> 1; if(t <= mid) add(ls, l, mid, t, c, val); else add(rs, mid + 1, r, t, c, val); sum[x] = sum[ls] + sum[rs]; cnt[x] = cnt[ls] + cnt[rs]; } int ask(int x, int l, int r, int k){ if(l == r) return min(cnt[x], k) * l; int mid = (l + r) >> 1; if(k <= cnt[ls]) return ask(ls, l, mid, k); else return sum[ls] + ask(rs, mid + 1, r, k - cnt[ls]); } }NEG, POS; int A, B; int neg, pos; inline void update(int i, int k){ if(a[i] < 0){ NEG.add(1, 0, m, -a[i], k * 2 * l[i], -a[i]); A += k * a[i] * (-l[i]); neg += k * l[i]; } else{ POS.add(1, 0, m, a[i], k * 2 * l[i], a[i]); A += k * a[i] * l[i]; pos += k * l[i]; } } signed main(){ n = read(), m = read(), q = read(); for(int i = 0; i < n; i++) l[i] = read(); for(int i = 0; i < m; i++){ int x = read(), y = read(); ++s[x], --s[y]; } for(int i = n - 2; i >= 0; i--) a[i] = a[i + 1] + s[i + 1]; for(int i = 0; i < n; i++) update(i, 1); for(; q; q--){ int i = read(); update(i, -1); l[i] = read(); update(i, 1); if(neg > pos) B = NEG.ask(1, 0, m, neg - pos); else B = POS.ask(1, 0, m, pos - neg); print(A - B), putchar('\n'); } return 0; } ```