【杂题题解】ABC467 E - Adjacent Sums (hard)

洛谷传送门:https://www.luogu.com.cn/problem/AT_abc467_e

AT 传送门:https://atcoder.jp/contests/abc467/tasks/abc467_e

这道题的本质是:我们只能把每个数增加,所以对于最终余数 \(r_i\),最小增加次数为

\[(r_i - A_i) \bmod M \]

其中取模结果在 \([0, M-1]\)

而条件只和余数有关:

\[(r_i + r_{i+1}) \bmod M = B_i \]

一旦确定了第一个数的余数 \(r_1 = t\),后面所有 \(r_i\) 就唯一确定了:

\[r_i \equiv u_i + (-1)^{i-1} t \pmod M \]

其中 \(u_1 = 0\),并且递推:

\[u_i = (B_{i-1} - u_{i-1}) \bmod M \]

也就是说:

  • \(i\) 为奇数,\(r_i \equiv u_i + t \pmod M\)
  • \(i\) 为偶数,\(r_i \equiv u_i - t \pmod M\)

\[w_i = (u_i - A_i) \bmod M \]

那么位置 \(i\) 的代价就是

\[(w_i + s_i t) \bmod M \]

其中

\[s_i = \begin{cases} +1, & i \text{ 为奇数} \\ -1, & i \text{ 为偶数} \end{cases} \]

所以总代价为

\[F(t) = \sum_{i=1}^{N} (w_i + s_i t) \bmod M \]

我们需要在 \(t\in [0, M-1]\) 中最小化 \(F(t)\)

把奇偶位置分开:

  • 奇数位置 \(i\)

    \[(w_i + t) \bmod M = \begin{cases} w_i + t, & w_i + t < M \\ w_i + t - M, & w_i + t \ge M \end{cases} \]

    等价于:先加 \(t\),但如果 \(w_i \ge M - t\),就再减一个 \(M\)

  • 偶数位置 \(i\)

    \[(w_i - t) \bmod M = \begin{cases} w_i - t, & w_i \ge t \\ w_i - t + M, & w_i < t \end{cases} \]

    等价于:先减 \(t\),但如果 \(w_i < t\),就再加一个 \(M\)

因此:

\[F(t) = \sum w_i + (\text{奇数个数} - \text{偶数个数}) t - M \times \#\{奇数 i \mid w_i \ge M - t\} + M \times \#\{偶数 i \mid w_i < t\} \]

注意:

\[\text{奇数个数} - \text{偶数个数} = N \bmod 2 \]

要么是 \(0\),要么是 \(1\)

\(F(t)\) 看成关于 \(t\) 的函数,随着 \(t\) 增加:

  • 奇数项对斜率的贡献是 \(+1\)
  • 偶数项对斜率的贡献是 \(-1\)

所以区间内斜率恒为:

\[\text{奇数个数} - \text{偶数个数} \in \{0, 1\} \]

也就是说,在两个"拐点"之间,\(F(t)\) 不会下降,只可能不变或上升,我们需要枚举的值就在拐点上。

拐点发生在某个 \(w_i + s_i t\) 刚好等于 \(0\)\(M\) 的地方,也就是某个位置的代价刚好为 \(0\) 的情况:

  • 若 i 为奇数,令 \(w_i + t \equiv 0 \pmod M\),解得

    \[t \equiv A_i - u_i \pmod M \]

  • 若 i 为偶数,令 \(w_i - t \equiv 0 \pmod M\),解得

    \[t \equiv u_i - A_i \pmod M \]

所以只需要枚举这 \(N\) 个候选 \(t\) 即可。

预处理:

  • 把所有奇数位置的 \(w_i\) 放入数组 \(p1\),排序;
  • 把所有偶数位置的 \(w_i\) 放入数组 \(p0\),排序;
  • 计算 \(sum = \sum w_i\)

对于给定的 \(t\)

  • 奇数部分需要减去 \(M\) 的个数:

    id = lower_bound(p1, M - t) - p1;
    cnt = cnt1 - id + 1;
    res -= cnt * M;
    
  • 偶数部分需要加上 \(M\) 的个数:

    id = lower_bound(p0, t) - p0 - 1;
    // id 就是 w_i < t 的个数
    res += id * M;
    

再加上基础值:

res = sum + (n % 2) * t;

枚举所有候选 \(t\),取最小值即可。

#include <bits/stdc++.h>
using namespace std;

using ll = long long;

const int N = 2e5 + 10;
const ll inf = 0x3f3f3f3f3f3f3f3f;

ll n, M, a[N], b[N];
ll u[N], p1[N], p0[N];
ll cnt1, cnt0;

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    cin >> n >> M;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    
    for (int i = 1; i < n; i++)
        cin >> b[i];

    u[1] = 0;
    for (int i = 2; i <= n; i++)
        u[i] = (b[i - 1] - u[i - 1] + M) % M;
    
    ll alsum = 0ll;
    for (int i = 1; i <= n; i++) {
        if (i & 1) {
            p1[++cnt1] = (u[i] - a[i] + M) % M;
            alsum += p1[cnt1];
        } else {
            p0[++cnt0] = (u[i] - a[i] + M) % M;
            alsum += p0[cnt0];
        }
    }

    sort(p1 + 1, p1 + cnt1 + 1);
    sort(p0 + 1, p0 + cnt0 + 1);

    ll ans = inf;
    for (int i = 1; i <= n; i++) {
        ll t = 0;
        if (i & 1) t = (a[i] - u[i] + M) % M;
        else t = (u[i] - a[i] + M) % M;

        ll res = alsum + (n % 2) * t;

        int id = lower_bound(p1 + 1, p1 + cnt1 + 1, M - t) - p1;
        res -= (cnt1 - id + 1LL) * M;

        id = lower_bound(p0 + 1, p0 + cnt0 + 1, t) - p0 - 1;
        res += (id + 0LL) * M;

        ans = min(ans, res);
    }
    
    cout << ans << "\n";
    return 0;
}
posted @ 2026-07-21 21:37  chrispang  阅读(1)  评论(0)    收藏  举报