QOJ #10042. Scheduling 题解

Description

给定 \(n\) 个二元组 \((l_i,r_i)\),请你构造一个长度为 \(n\) 的序列 \(a\),满足:

  • \(\forall 1\le i\le n\)\(l_i\le a_i\le r_i\)
  • \(\forall 1\le i<j\le n\)\(|a_i-a_j|\ge2\)

\(T\) 组数据。

\(1\le T\le 100,1\le \sum n\le 2\times 10^5,1\le l_i\le r_i\le 10^6\)

Solution

首先如果只要求互不相同就是个经典的贪心,对区间按照 \(r\) 排序,每次选择 \(\geq l_i\) 的最小的数选。

但是这里显然不能这么做。考虑通过构造候选集合,来转化成上面的那个贪心。

即构造一个序列 \(b_1,b_2,\ldots,b_m\),满足 \(\forall 1\leq i<j\leq m,|b_i-b_j|\geq 2\) 且满足最终的答案都在这里面选。

注意到如果给定 \(b\) 序列,这就是个单点匹配区间的问题,可以用 Hall 定理判断有无解。

所以考虑 Hall 定理。

\(F(l,r)\) 表示包含于 \([l,r]\) 的询问区间数,\(G(l,r)=r-l+1-2\cdot F(l,r)\),可以用线段树+扫描线维护 \(G(l,r)\) 的值。

那么如果存在 \(l,r\),使得 \(G(l,r)\leq -2\),那么无论怎么填都不能满足这个区间的限制,也就是无解。

如果 \(G(l,r)=-1\),那么这个区间一定是填形如 \(1010101\) 这样一个 \(1\) 一个 \(0\) 交错的形式。

其余的先不管。

把所有满足 \(G(l,r)=-1\) 的区间拿出来后,已经可以确定一部分位置的答案了,把这些已知的为 \(1\) 的位置 \(c_1,c_2,\ldots,c_k\) 拿出来后容易发现不同区间之间的填法基本上是独立的,考虑从前往后对于 \([c_i+1,c_{i+1}-1]\) 分别做。

如果 \((c_{i+1}-c_i)\bmod 2=0\),那么这个区间一定填的是 \(010101\ldots 010\),其余的一定不优。

如果 \((c_{i+1}-c_i)\bmod 2=1\),注意到现在不能像上面那样去完全交错填了,即一定存在一个 \(g\),满足 \(g\)\(g+1\) 都是 \(0\),可以发现 \([c_i,g-1]\)\([g+2,c_{i+1}]\) 都交错着填一定更优。

由于 \(g\) 越小对于后面的区间一定更优,所以只需要对于当前区间找到最小的能够满足所有 \(r\leq c_{i+1}\) 的区间的限制的 \(g\) 即可。

\(H(l,r)\) 表示 \([l,r] 没有确定答案的点数+2\times \left([l,r] 中确定的 1 的个数-包含于 [l,r] 的区间数\right)\),容易发现所有没有确定的点一定都在 \((c_i,c_{i+1})\) 内。

考虑从前往后扫右端点 \(r\),找到满足 \(H(l,r)\) 最小的最小的 \(l\)

  • 如果 \(H(l,r)<0\),显然 \(l\leq c_i\),否则 \(l\) 一定会被加到 \(c\) 数组里。由于 \(c_i+1\) 一定填 \(0\),所以无论如何也不能满足 \([l,r]\) 的限制。

  • 如果 \(H(l,r)=0\),则 \(g\)\(g+1\) 出现在 \([l,r]\) 里会让 \(H(l,r)\) 减为负数,不满足条件,给 \([l,r]\) 打上标记表示 \(g\) 不能出现在里面。

  • 如果 \(H(l,r)>0\),则 \(g\) 在哪里 \([l,r]\) 都能满足条件,不管它。

最后 \((c_i,c_{i+1})\) 里找最小的与 \(c_{i+1}\) 奇偶性相同且没被打上标记的位置就是 \(g\),然后更新线段树。


注意多组数据 \(\sum r_i\) 是没有限制的,所以需要离散化。具体的做法是按照 \(l\) 排序,然后每次找到 \(\geq l_i\)\(3\) 没选的位置加进去,最后对 \(l,r\) 分别二分找到对应取值,再做上面的东西。

时间复杂度:\(O(n\log n)\)

Code

#include <bits/stdc++.h>

// #define int int64_t

using pii = std::pair<int, int>;

const int kMaxN = 6e5 + 5;

int n, m, cnt;
int a[kMaxN], res[kMaxN], l[kMaxN], r[kMaxN], unq[kMaxN];
std::vector<int> vv[kMaxN];

struct SGT {
  pii mi[kMaxN * 4];
  int tag[kMaxN * 4];
  void pushup(int x) {
    mi[x] = std::min(mi[x << 1], mi[x << 1 | 1]);
  }
  void addtag(int x, int v) {
    mi[x].first += v, tag[x] += v;
  }
  void pushdown(int x) {
    if (tag[x]) {
      addtag(x << 1, tag[x]), addtag(x << 1 | 1, tag[x]);
      tag[x] = 0;
    }
  }
  void build(int x, int l, int r) {
    tag[x] = 0;
    if (l == r) return void(mi[x] = {0, l});
    int mid = (l + r) >> 1;
    build(x << 1, l, mid), build(x << 1 | 1, mid + 1, r);
    pushup(x);
  }
  void update(int x, int l, int r, int ql, int qr, int v) {
    if (l > qr || r < ql) return;
    else if (l >= ql && r <= qr) return addtag(x, v);
    pushdown(x);
    int mid = (l + r) >> 1;
    update(x << 1, l, mid, ql, qr, v), update(x << 1 | 1, mid + 1, r, ql, qr, v);
    pushup(x);
  }
  pii query(int x, int l, int r, int ql, int qr) {
    if (l > qr || r < ql) return {1e9, 0};
    else if (l >= ql && r <= qr) return mi[x];
    pushdown(x);
    int mid = (l + r) >> 1;
    return std::min(query(x << 1, l, mid, ql, qr), query(x << 1 | 1, mid + 1, r, ql, qr));
  }
} sgt;

void discrete() {
  std::vector<int> vec;
  for (int i = 1; i <= n; ++i) vec.emplace_back(l[i]);
  std::sort(vec.begin(), vec.end());
  int p = 0;
  m = 0;
  for (auto x : vec) {
    for (int i = 1; i <= 3; ++i) {
      unq[++m] = p = std::max(p + 1, x);
    }
  }
  for (int i = 1; i <= m; ++i) std::vector<int>().swap(vv[i]);
  for (int i = 1; i <= n; ++i) {
    l[i] = std::lower_bound(unq + 1, unq + 1 + m, l[i]) - unq;
    r[i] = std::lower_bound(unq + 1, unq + 1 + m, r[i]) - unq - 1;
    vv[r[i]].emplace_back(l[i]);
  }
}

bool getarr() {
  static int diff[kMaxN][2];
  cnt = 0, sgt.build(1, 1, m);
  for (int i = 0; i <= m; ++i) diff[i][0] = diff[i][1] = 0;
  for (int i = 1; i <= m; ++i) {
    sgt.update(1, 1, m, 1, i, 1);
    for (auto x : vv[i]) sgt.update(1, 1, m, 1, x, -2);
    auto p = sgt.query(1, 1, m, 1, i);
    if (p.first < -1) return 0;
    if (p.first == -1) {
      int j = p.second;
      ++diff[j][1], --diff[i + 2][1];
      ++diff[j + 1][0], --diff[i + 1][0];
    }
  }
  for (int i = 1; i <= m; ++i) {
    if (i >= 2) diff[i][0] += diff[i - 2][0], diff[i][1] += diff[i - 2][1];
    if (diff[i][0] && diff[i][1]) return 0;
    if (diff[i][1]) a[++cnt] = i;
  }
  int lst = cnt;
  sgt.build(1, 1, m);
  if (!cnt) {
    for (int i = 1; i <= m; i += 2) a[++cnt] = i;
    return 1;
  }
  for (int i = 1; i <= a[1]; ++i) {
    for (auto x : vv[i]) sgt.update(1, 1, m, 1, x, -2);
    if (i < a[1] && i % 2 == a[1] % 2)
      a[++cnt] = i, sgt.update(1, 1, m, 1, i, 2);
  }
  sgt.update(1, 1, m, 1, a[1], 2);
  for (int i = 1; i < lst; ++i) {
    int p = a[i], q = a[i + 1];
    if (q - p <= 1) return 0;
    if (p % 2 == q % 2) {
      for (int j = p + 2; j < q; j += 2)
        a[++cnt] = j, sgt.update(1, 1, m, 1, j, 2);
      for (int j = p + 1; j <= q; ++j)
        for (auto x : vv[j])
          sgt.update(1, 1, m, 1, x, -2);
      sgt.update(1, 1, m, 1, q, 2);
    } else {
      static int tmp[kMaxN];
      for (int j = p; j <= q; ++j) tmp[j] = 0;
      for (int j = p + 1; j <= q; ++j) {
        sgt.update(1, 1, m, 1, j, 1 + (j == q));
        for (auto x : vv[j]) sgt.update(1, 1, m, 1, x, -2);
        auto p = sgt.query(1, 1, m, 1, j);
        if (p.first < 0) return 0;
        if (p.first == 0) {
          ++tmp[std::max(p.second, a[i])], --tmp[j];
        }
      }
      for (int j = p + 1; j <= q; ++j) tmp[j] += tmp[j - 1];
      int gap = -1;
      for (int j = p + 1; j < q; ++j) {
        if (j % 2 != p % 2 && !tmp[j]) { gap = j; break; }
      }
      if (!~gap) return 0;
      for (int j = p + 1; j <= q; ++j) sgt.update(1, 1, m, 1, j, -(1 + (j == q)));
      for (int j = p + 2; j < gap; j += 2)
        a[++cnt] = j, sgt.update(1, 1, m, 1, j, 2);
      for (int j = gap + 2; j < q; j += 2)
        a[++cnt] = j, sgt.update(1, 1, m, 1, j, 2);
      sgt.update(1, 1, m, 1, q, 2);
    }
  }
  for (int i = a[lst] + 2; i <= m; i += 2) a[++cnt] = i;
  std::sort(a + 1, a + 1 + cnt);
  return 1;
}

bool getres() {
  std::vector<int> vec;
  for (int i = 1; i <= n; ++i) vec.emplace_back(i);
  std::sort(vec.begin(), vec.end(), [&] (int x, int y) { return r[x] < r[y] || r[x] == r[y] && l[x] > l[y]; });
  std::set<int> st;
  for (int i = 1; i <= cnt; ++i) st.emplace(a[i]);
  for (auto i : vec) {
    auto it = st.lower_bound(l[i]);
    if (it != st.end() && *it <= r[i]) {
      res[i] = *it, st.erase(it);
    } else {
      return 0;
    }
  }
  return 1;
}

void dickdreamer() {
  std::cin >> n;
  for (int i = 1; i <= n; ++i) std::cin >> l[i] >> r[i];
  discrete();
  if (!getarr() || !getres()) return void(std::cout << "-1\n");
  for (int i = 1; i <= n; ++i) std::cout << unq[res[i]] << " \n"[i == n];
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}
posted @ 2025-03-29 15:12  下蛋爷  阅读(71)  评论(0)    收藏  举报