1. 程式人生 > 其它 >LOJ #3006. 「JOISC 2015 Day 4」防壁

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

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

​ 首先有一個很顯然的結論是:對於每條線段,貪心地向詢問點移動直到覆蓋的方案一定是最優的。於是我們就得到了一個 \(\mathcal O(NM)\) 的暴力做法。

​ 我們先考慮Subtask2也就是 \(a_i=0\) 的情況怎麼做。對於線段 \([0,t]\) 我們定義詢問點 \(x_i\) 是重要的當且僅當該線段在為了覆蓋 \(x_i\) 時做出了貢獻,否則則稱 \(x_i\) 是非重要的。關於這個定義容易有以下結論:

  1. 對於一個詢問點 \(x_i\) 如果其對於線段 \([0,t]\) 是重要的,那麼 \(x_i\) 對於線段 \([0,t-1]\)
    也是重要的。
  2. 如果詢問點 \(x_i\) 對線段 \([0,t]\) 是重要的,並且滿足關係 \(x_{i-1}<x_i\),那麼該線段在覆蓋了 \(x_i\) 之後的位置是 \([x_i-t,x_i]\);反之若滿足關係 \(x_{i-1}>x_i\),那麼位置就是 \([x_i,x_i+t]\)

對於詢問點 \(x_i\) 我們定義 \(f(x_i)\) 是滿足”\(x_i\) 對線段 \([0,t]\) 是重要的“的最大的 \(t\)。先考慮如何求出 \(f(x_i)\)。注意到 \(f(x_i)\) 的極值性,我們考慮整體二分。對於線段 \([0,t]\) 我們要判斷 \(x_i\)

是否重要,那就必須要求出該線段在詢問點 \(x_i\) 之前的位置,也就是上一個對線段 \([0,t]\) 重要的詢問點 \(x_i'\);考慮到整體二分過程中右邊有對左邊的貢獻,用一個數組維護這個值即可。

​ 然後我們思考在求出 \(f(x_i)\) 之後如何求出答案。假設當前的線段為 \([0,t]\),容易發現若詢問點 \(x_i\) 對該線段是非重要的,那麼它對這個線段沒有任何影響。於是只需要考慮所有對 \([0,t]\) 重要的詢問點 \(x_i\) 組成的詢問點序列 \(y_1\ldots y_m\)。容易發現 \(y_1\ldots y_m\)\([0,t]\) 都有貢獻,不妨先假設線段 \([0,t]\)

移動相鄰的詢問點 \(y_i\rightarrow y_{i+1}\) 都需要移動 \(|y_{i+1}-y_i|\) 格,而若 \([y_{i-1}<y_i]\neq [y_i<y_{i+1}]\) 即詢問點序列在 \(i\) 處形成了一個拐點,那麼線段 \([0,t]\) 就可以少移動 \(t\) 格。如果設序列 \(y\) 中拐點的數量為 \(c\),那麼線段 \([0,t]\) 的答案為 \(\sum\limits_{i=1}^m|y_i-y_{i-1}|-c\cdot t\)。我們把所有的線段 \([0,t_i]\) 按長度排序,然後動態維護詢問點序列 \(y_1\ldots y_m\) 和其中拐點的數量。這樣Subtask2就做完了,時間複雜度是整體二分的複雜度 \(\mathcal O(M\log N)\)

​ 然後我們考慮一般情況 \([a_i,b_i]\)。設 \(t_i=b_i-a_i\) 我們考慮算出 \([a_i,b_i]\) 的答案減去 \([0,t_i]\) 的答案 ( 記其為 \(w\) ) 於是觀察線段 \([a_i,b_i]\)\([0,t_i]\) 的移動。找到最小的 \(p\) 滿足存在 \(q<p\) 使得 \(|x_p-x_q|>t_i\)。若不存在這樣的 \(p\) 此時線段 \([a_i,b_i]\)\([0,t_i]\) 只會分別往一個方向移動,這是很容易的。對於新定義的 \(p\) 我們可以發現一些結論:

  1. 在覆蓋完詢問點 \(x_p\) 之後線段 \([a_i,b_i]\)\([0,t_i]\) 位置一樣;
  2. 在覆蓋詢問點 \(x_p\) 之前線段 \([a_i,b_i]\)\([0,t_i]\) 只會分別往一個方向移動。

於是我們直接二分求出 \(p\) 之後就可以很容易算出 \(w\) 了。這樣一般情況也做完了。

​ 時間複雜度為 \(\mathcal O(N\log M+M\log N)\),空間複雜度為 \(\mathcal 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