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

posted on 2024-09-11 20:28  UXOD  阅读(13)  评论(0编辑  收藏  举报