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;
}

浙公网安备 33010602011771号