可以发现,如果我们枚举每个理想亮度 \(X\) 然后再求在这个理想亮度情况下的答案是非常难维护的。
不妨反过来,考虑每个位置 \(i, i + 1\) 之间对每个理想亮度 \(X\) 减少次数的贡献。
不难发现需要分两种情况讨论:
-
若 \(a_i < a_{i + 1}\),那么此时只会对 \(a_i + 2 \sim a_{i + 1}\) 产生贡献 \(1, 2, \cdots a_{i + 1} – a_i – 1\)
-
若 \(a_i > a_{i + 1}\),那么此时只会对 \(a_{i} + 2 \sim m, 1 \sim a_{i + 1}\) 产生贡献 \(1, 2, \cdots m – (a_{i} – a_{i + 1} – 1)\)
注意到只需要最后查询一次,每次修改只需添加一段等差数列直接使用二次差分即可。
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; ++i)
const int N = 1e5 + 5;
int n, m, ans, del, a[N], d1[N], d2[N];
int read() {
char c; int x = 0, f = 1;
c = getchar();
while (c > '9' || c < '0') { if(c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
void update(int x, int y) {
if(x < y) ++d1[x + 2], --d1[y + 1], d2[y + 1] -= y - x - 1;
else ++d1[x + 2], d1[1] += m - x, d1[2] += 1 - (m - x), --d1[y + 1], d2[y + 1] -= m + y - x - 1;
}
signed main() {
n = read(), m = read();
rep(i, 1, n) a[i] = read();
rep(i, 1, n - 1) ans += (a[i] > a[i + 1]) * m + a[i + 1] - a[i];
rep(i, 1, n - 1) update(a[i], a[i + 1]);
rep(i, 1, m) d1[i] += d1[i - 1];
rep(i, 1, m) d1[i] += d1[i - 1] + d2[i];
rep(i, 1, m) del = max(del, d1[i]);
printf("%lld", ans - del);
return 0;
}
当需要考虑在每种情况下答案分别为多少的时候,一种方式是考虑使用某种方法动态维护每次的答案;另一种方式基于答案来源的线性性,以便于可以考虑每个部分对每种情况答案的贡献。