atc abc467E 思路分享(扫描线)

https://atcoder.jp/contests/abc467/tasks/abc467_e

题意

给定长为 \(n\) 序列 \(a\) 和长为 \(n-1\) 序列 \(b\),每次操作令 \(a_i \leftarrow a_i + 1 \pmod m\),求使得 \(b_i = a_i + a_{i+1} \pmod m\) 的最少操作次数.

\(2\le n \le 2\cdot 10^5\)\(3 \le m \le 10^9\).

思路

操作对 \(b\) 的影响是选两个相邻位置 \(+1\),边界位置可以单独选,令 \(c_i = b_i - (a_i + a_{i+1}) \pmod m\),问题转化成对初始全 \(0\) 的序列操作,目标序列是 \(c\).

可以发现,钦定对 \(1\) 位置单独操作 \(t\) 次后,其他位置的操作次数确定,记为 \(x_i\),则:

\[\begin{cases} x_1 = c_1-t \\ x_2 = c_2-c_1+t \\ x_3 = c_3-c_2+c_1-t \\ \cdots \end{cases} \]

\(p_i\) 为除去 \(t\) 的部分,则 \(p_i = c_i-p_{i-1}\),因此:

\[x_i = \begin{cases} p_i + t \pmod m,i \text{偶} \\ p_i - t \pmod m,i \text{奇} \end{cases} \]

考察 \(t\rightarrow t+1\) 的变化.

  • \(i\) 为奇数,基础增量为 \(-1\),当 \(t=p_i\) 时突变,额外增量为 \(m\).

  • \(i\) 为偶数,基础增量为 \(+1\),当 \(t=m-p_i-1\) 时突变,额外增量为 \(-m\).

对所有突变事件做扫描线,只需遍历 \(n\) 个事件即可.

时间复杂度 \(\mathcal{O}(n\log n)\).

代码

//author:kzssCCC

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

const ll INF = 9e18;

void solve(){
    int n,m;
    cin >> n >> m;
    n--;
    vector<ll> a(n+2);
    for (int i=1;i<=n+1;i++){
        cin >> a[i];
    } 
    vector<ll> b(n+1),c(n+1);
    for (int i=1;i<=n;i++){
        cin >> b[i];
        c[i] = (b[i]-(a[i]+a[i+1])%m+m)%m;
    }

    int base = n&1?-1:0;
    vector<ll> p(n+1);
    p[1] = c[1];
    for (int i=2;i<=n;i++){
        p[i] = (c[i]-p[i-1]+m)%m;
    }

    ll sum = accumulate(p.begin()+1,p.end(),0ll);
    ll minn = INF;

    vector<pair<int,int>> ev;
    for (int i=1;i<=n;i++){
        if (i&1 && p[i]<m-1){
            ev.emplace_back(p[i],m);
        }
        if (i%2==0 && p[i]>0){
            ev.emplace_back(m-p[i]-1,-m);
        }
    }
    ev.emplace_back(m-1,0);
    sort(ev.begin(),ev.end());
    int len = ev.size();
    ll cur = sum;
    int t = 0;
    
    for (int i=0;i<len;i++){
        minn = min(minn,t+cur);
        int pos = ev[i].first;
        while (i<len && ev[i].first==pos){
            cur += ev[i].second;
            i++;
        }
        i--;
        cur += base*(ev[i].first+1-t);
        t = ev[i].first+1;
    }

    cout << minn << '\n';
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int t = 1;
    // cin >> t;
    while (t--) solve();

    return 0;
}
posted @ 2026-07-21 12:52  kzssCCC  阅读(3)  评论(0)    收藏  举报