LOJ #3006. 「JOISC 2015 Day 4」防壁

LOJ #3006. 「JOISC 2015 Day 4」防壁

​ 首先有一个很显然的结论是:对于每条线段,贪心地向询问点移动直到覆盖的方案一定是最优的。于是我们就得到了一个 O(NM) 的暴力做法。

​ 我们先考虑Subtask2也就是 ai=0 的情况怎么做。对于线段 [0,t] 我们定义询问点 xi 是重要的当且仅当该线段在为了覆盖 xi 时做出了贡献,否则则称 xi 是非重要的。关于这个定义容易有以下结论:

  1. 对于一个询问点 xi 如果其对于线段 [0,t] 是重要的,那么 xi 对于线段 [0,t1] 也是重要的。
  2. 如果询问点 xi 对线段 [0,t] 是重要的,并且满足关系 xi1<xi,那么该线段在覆盖了 xi 之后的位置是 [xit,xi];反之若满足关系 xi1>xi,那么位置就是 [xi,xi+t]

对于询问点 xi 我们定义 f(xi) 是满足”xi 对线段 [0,t] 是重要的“的最大的 t。先考虑如何求出 f(xi)。注意到 f(xi) 的极值性,我们考虑整体二分。对于线段 [0,t] 我们要判断 xi 是否重要,那就必须要求出该线段在询问点 xi 之前的位置,也就是上一个对线段 [0,t] 重要的询问点 xi;考虑到整体二分过程中右边有对左边的贡献,用一个数组维护这个值即可。

​ 然后我们思考在求出 f(xi) 之后如何求出答案。假设当前的线段为 [0,t],容易发现若询问点 xi 对该线段是非重要的,那么它对这个线段没有任何影响。于是只需要考虑所有对 [0,t] 重要的询问点 xi 组成的询问点序列 y1ym。容易发现 y1ym[0,t] 都有贡献,不妨先假设线段 [0,t] 移动相邻的询问点 yiyi+1 都需要移动 |yi+1yi| 格,而若 [yi1<yi][yi<yi+1] 即询问点序列在 i 处形成了一个拐点,那么线段 [0,t] 就可以少移动 t 格。如果设序列 y 中拐点的数量为 c,那么线段 [0,t] 的答案为 i=1m|yiyi1|ct。我们把所有的线段 [0,ti] 按长度排序,然后动态维护询问点序列 y1ym 和其中拐点的数量。这样Subtask2就做完了,时间复杂度是整体二分的复杂度 O(MlogN)

​ 然后我们考虑一般情况 [ai,bi]。设 ti=biai 我们考虑算出 [ai,bi] 的答案减去 [0,ti] 的答案 ( 记其为 w ) 于是观察线段 [ai,bi][0,ti] 的移动。找到最小的 p 满足存在 q<p 使得 |xpxq|>ti。若不存在这样的 p 此时线段 [ai,bi][0,ti] 只会分别往一个方向移动,这是很容易的。对于新定义的 p 我们可以发现一些结论:

  1. 在覆盖完询问点 xp 之后线段 [ai,bi][0,ti] 位置一样;
  2. 在覆盖询问点 xp 之前线段 [ai,bi][0,ti] 只会分别往一个方向移动。

于是我们直接二分求出 p 之后就可以很容易算出 w 了。这样一般情况也做完了。

​ 时间复杂度为 O(NlogM+MlogN),空间复杂度为 O(n)。由于所有带 log 的地方都是二分,因此该做法的常数较小。

参考代码
#include <bits/stdc++.h>
using namespace std;
static constexpr int inf = 0x3f3f3f3f;
static constexpr int Maxn = 2e5 + 5;
int n, m, a[Maxn], b[Maxn];
pair<int, int> c[Maxn], q[Maxn];
int64_t ans[Maxn];
int p[Maxn], dir[Maxn], pmn[Maxn], pmx[Maxn], pdif[Maxn];
int qi[Maxn], pt[Maxn], pre[Maxn];
void divide(int l, int r, int ql, int qr) {
  if (l > r || ql > qr) return ;
  if (l == r) {
    for (int i = ql; i <= qr; ++i) pt[qi[i]] = l;
  } else {
    int mid = (l + r + 1) / 2, qn1 = 0, qn2 = 0;
    static int q1[Maxn], q2[Maxn];
    const int len = c[mid].first;
    int xl = 0, xr = len, t = 0;
    for (int i = ql; i <= qr; ++i) {
      if (pre[qi[i]] > t) {
        t = pre[qi[i]];
        if ( dir[t]) xr = p[t], xl = xr - len;
        if (!dir[t]) xl = p[t], xr = xl + len;
      }
      if (xr < p[qi[i]] || xl > p[qi[i]]) {
        t = q2[++qn2] = qi[i];
        if (xr < p[t]) xr = p[t], xl = xr - len;
        if (xl > p[t]) xl = p[t], xr = xl + len;
      } else q1[++qn1] = qi[i], pre[qi[i]] = t;
    }
    for (int i = 1; i <= qn1; ++i) qi[ql + i - 1] = q1[i];
    for (int i = 1; i <= qn2; ++i) qi[ql + qn1 + i - 1] = q2[i];
    divide(l, mid - 1, ql, qr - qn2);
    divide(mid, r, qr - qn2 + 1, qr);
  }
} // divide
int main(void) {
  extern uint32_t readu32(void);
  n = readu32(), m = readu32();
  for (int i = 1; i <= n; ++i) {
    a[i] = readu32(), b[i] = readu32();
    c[i] = pair(b[i] - a[i], i);
  } sort(c + 1, c + n + 1);
  for (int i = 1; i <= m; ++i)
    p[i] = readu32(), dir[i] = (p[i] > p[i - 1]);
  iota(qi + 1, qi + m + 1, 1), divide(0, n, 1, m);
  for (int i = 1; i <= m; ++i) q[i] = pair(pt[i], i);
  sort(q + 1, q + m + 1);
  int64_t cur = 0, turn = 0;
  auto upd = [&](int x, int y, int w) {
    if (x > m || y > m) return;
    cur += w * abs(p[x] - p[y]);
    turn += w * (dir[x] != dir[y]);
  }; // main()::upd
  auto calc = [&](int x) { return cur - turn * x; };
  for (int i = 1; i <= m; ++i) upd(i - 1, i, 1);
  static int prv[Maxn], nxt[Maxn];
  for (int i = 1; i <= m; ++i) prv[i] = i - 1;
  for (int i = 1; i <= m; ++i) nxt[i] = i + 1;
  for (int i = 1, j = 1; i <= n; ++i) {
    while (j <= m && q[j].first < i) {
      int x = q[j].second;
      int l = prv[x], r = nxt[x];
      upd(l, x, -1), upd(r, x, -1);
      nxt[l] = r, prv[r] = l;
      upd(l, r, +1); ++j;
    } ans[c[i].second] = calc(c[i].first);
  }
  pmn[0] = inf, pmx[0] = 0;
  for (int i = 1; i <= m; ++i) {
    pmn[i] = min(pmn[i - 1], p[i]);
    pmx[i] = max(pmx[i - 1], p[i]);
    pdif[i] = pmx[i] - pmn[i];
  }
  for (int i = 1; i <= n; ++i) {
    const int len = b[i] - a[i];
    int j = upper_bound(pdif + 1, pdif + m + 1, len) - pdif;
    if (j == m + 1) {
      auto inter = [&](int l1, int r1, int l2, int r2)
        { return max<int>(0, max(r2 - r1, l1 - l2)); };
      int ori = inter(a[i], b[i], pmn[m], pmx[m]);
      int cur = inter(0, len, pmn[m], pmx[m]);
      ans[i] += ori - cur;
    } else {
      auto calc = [&](int l, int r) {
        int64_t res = 0;
        if (l > pmn[j - 1]) res += l - pmn[j - 1], l = pmn[j - 1], r = l + len;
        else if (r < pmx[j - 1]) res += pmx[j - 1] - r, r = pmx[j - 1], l = r - len;
        if (l > pmn[j]) res += l - pmn[j], l = pmn[j], r = l + len;
        else if (r < pmx[j]) res += pmx[j] - r, r = pmx[j], l = r - len;
        return res;
      };
      ans[i] += calc(a[i], b[i]) - calc(0, len);
    }
  }
  for (int i = 1; i <= n; ++i)
    printf("%lld\n", ans[i]);
  exit(EXIT_SUCCESS);
} // main
// fast io
static const int _BUF_SIZE = 1 << 18;
static char _ibuf[_BUF_SIZE], *iS = _ibuf, *iT = _ibuf;
inline char getch(void) {
  if (__builtin_expect(iS == iT, false))
    iT = (iS = _ibuf) + fread(_ibuf, 1, _BUF_SIZE, stdin);
  if (__builtin_expect(iS == iT, false)) return EOF;
  else return *iS++;
} // getch
uint32_t readu32(void) {
  register uint32_t x = 0;
  register char ch = getch();
  while (ch < '0' || ch > '9') ch = getch();
  while (ch >= '0' && ch <= '9') ((x += (x << 2)) <<= 1) += (ch ^ '0'), ch = getch();
  return x;
} // readu32
posted @   cutx64  阅读(197)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示