其他分享
首页 > 其他分享> > 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

标签:LOJ,线段,pmx,len,JOISC,int,3006,Maxn,qi
来源: https://www.cnblogs.com/cutx64/p/16057864.html