洛谷 P11021 [LAOI-6] 区间测速 题解
使用 multiset
multiset
可以看成一个序列,支持插入一个数或删除一个数,时间复杂度均为 \(O(\log n)\),且能始终保证序列中的数是有序的,而且序列中可以存在重复的数(而set
容器要求两两不同,且不保证有序)。
一个基本事实:速度最大的时刻必然出现在两个相邻点之间。例如从 A 点先到 B 点再到 C 点,则 \(v_{AC} \le \max(v_{AB},v_{BC})\)。所以我们只需考虑相邻两点间的速度就可以了,非相邻点之间的平均速度一定小于等于它。
先排序并处理出最初的相邻两点间的速度,全部丢进 multiset
。
每次询问要求我们修改某个点的时间,例如把上例的 B 点修改到 D 点和 E 点之间,相当于从 multiset
中删除 A 到 B 的速度和 B 到 C 的速度,再插入 A 到 C 的速度,再插入 D 到 B 的速度和 B 到 E 的速度。由于 multiset
的有序性,输出最大的那一个就行了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e5 + 5;
// 对应的 t 和 x
struct node {
ll t, x, id; // id 为初始下标
friend bool operator<(const node &a, const node &b) {
return a.t < b.t; // 按时间排序
}
} a[N];
// v 表示速度, t 表示该速度的起始时间 (防止两段不同时间的平均速度相同)
struct vt {
ll v, t;
friend bool operator<(const vt &a, const vt &b) {
if (a.v == b.v) return a.t < b.t; // 保证排序的稳定性
return a.v < b.v;
}
};
multiset<vt> q;
ll n, m, u, v, pos[N]; // 排序后下标会乱, 用 pos 记录排序前的下标
int main(void) {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cin >> n >> m;
for (ll i = 1; i <= n; i++) {
cin >> a[i].x >> a[i].t;
a[i].id = i; // 初始下标
}
sort(a + 1, a + 1 + n);
for (ll i = 1; i <= n; i++) pos[a[i].id] = i; // 排序后的下标对应
for (ll i = 1; i < n; i++) // 计算速度并丢进 multiset
q.insert({abs(a[i + 1].x - a[i].x) / (a[i + 1].t - a[i].t), a[i].t});
while (m--) {
cin >> u >> v;
ll d1, d2, i1, i2, i3;
d1 = abs(a[pos[u]].x - a[pos[u] - 1].x) / (a[pos[u]].t - a[pos[u] - 1].t);
q.erase({d1, a[pos[u] - 1].t});
d2 = abs(a[pos[u] + 1].x - a[pos[u]].x) / (a[pos[u] + 1].t - a[pos[u]].t);
q.erase({d2, a[pos[u]].t});
i1 = abs(a[pos[u] + 1].x - a[pos[u] - 1].x) / (a[pos[u] + 1].t - a[pos[u] - 1].t);
q.insert({i1, a[pos[u] - 1].t});
// 查找修改后的时间在哪一段, 二分即可
ll l = 1, r = n;
while (l < r) {
ll mid = (l + r + 1) >> 1;
a[mid].t < v ? l = mid : r = mid - 1;
}
// 除数不为 0, 不加全 RE
if (v - a[l].t != 0) {
i2 = abs(a[l].x - a[pos[u]].x) / (v - a[l].t);
q.insert({i2, a[l].t});
}
if (a[l + 1].t - v != 0) {
i3 = abs(a[l + 1].x - a[pos[u]].x) / (a[l + 1].t - v);
q.insert({i3, v});
}
cout << (*q.rbegin()).v << '\n'; // 输出最大值
// 撤销修改
q.erase({i2, a[l].t});
q.erase({i3, v});
q.insert({d1, a[pos[u] - 1].t});
q.insert({d2, a[pos[u]].t});
q.erase({i1, a[pos[u] - 1].t});
}
return 0;
}