题解:洛谷 P4231 三步必杀
洛谷 P4231 三步必杀
题意
有 \(n\) 个位置,有 \(m\) 次操作,每次操作如下:
- 将 \([l,r]\) 内的位置加上对应的以 \(s\) 为开头,\(e\) 为末尾的等差数列的对应位置。
求所有操作后每个位置上的值。
\(n\le10^7\),\(m\le3\times10^5\)。
解法
绝世好题!二阶差分!
对于这种区间求和的问题,很容易想到差分。
考虑先求出公差 \(d=\dfrac{e-s}{r-l}\)。
下面是差分的修改:
| \(l\) | \(l+1\) | \(\cdots\) | \(r-1\) | \(r\) | \(r+1\) |
|---|---|---|---|---|---|
| \(+s\) | \(+d\) | \(+d\) | \(+d\) | \(+d\) | \(-e\) |
但是显然这样需要区间加,直接差分维护需要带 \(\log\) 的数据结构。
考虑将中间的 \(+d\) 消除,可以发现 \((+d)-(-d)=0\),那么可以试着再做一次差分(考虑差分的定义就可以得到):
| \(l\) | \(l+1\) | \(\cdots\) | \(r\) | \(r+1\) | \(r+2\) |
|---|---|---|---|---|---|
| \(+s\) | \(+d-s\) | \(0\) | \(0\) | \(-e-d\) | \(+e\) |
这样可以发现中间变为了 \(0\),只需要对 \(l,l+1,r+1,r+2\) 的位置进行修改即可,这样单次复杂度即为 \(O(1)\)。
时间复杂度
- 遍历每一操作,\(O(m)\)。
- 单次操作,\(O(1)\)。
时间复杂度 \(O(m)\),可以通过。
代码
/**
* Problem: P4231 三步必杀
* Author: OIer_wst
* Date: 2025-07-09
*/
#include <bits/stdc++.h>
using lint = long long;
int main() {
std::cin.tie(0)->sync_with_stdio(0);
int n, m; std::cin >> n >> m;
std::vector<lint> a(n + 3);
for (lint l, r, s, e; m; --m) {
std::cin >> l >> r >> s >> e;
lint d = (e - s) / (r - l);
a[l] += s;
a[l + 1] += d - s;
a[r + 1] += -e - d;
a[r + 2] += e;
}
for (int i = 1; i <= n; ++i) a[i] += a[i - 1];
lint ans = 0, mx = 0;
for (int i = 1; i <= n; ++i) {
a[i] += a[i - 1];
ans ^= a[i];
mx = std::max(mx, a[i]);
}
std::cout << ans << " " << mx << std::endl;
return 0;
}

浙公网安备 33010602011771号