QOJ #967. Rectangle Painting 题解
Description
小 N 不会写代码,所以他只会简单数据结构,于是他找到了一个基础线段树练习题。
在数轴上,每个自然数点都有一个可重集 \(A_0, A_1, A_2, \ldots\),初始时均为空。
你要支持以下操作 \(q\) 次:
- \(x, a, b\):对 \(\forall a \le i \le b\),在 \(A_i\) 中插入元素 \(x\)。
- \(l, r\):求 \(\displaystyle \max_{l \le i \le r} \operatorname{mex} A_i\)。
由于这太简单了,所以这个题强制在线。
\(1\leq q\leq 10^5,0\leq a,b,l,r,x\leq 2\times 10^5\)。
Solution
首先考虑不强制在线怎么做。
对于单个集合,注意到向它里面插入元素 \(x\) 是不好维护 \(\text{mex}\) 的,但是如果是删除元素 \(x\),就可以直接让 \(\text{mex}\leftarrow\min(\text{mex},x)\)。
所以离线时可以先把所有集合最终的 \(\text{mex}\) 求出来,再倒着删除插入操作,并进行区间 \(\text{chkmin}\) 即可。
回到这道题。
考虑对于每个 \(x\),用线段树计算它对哪些集合造成贡献。
然后维护集合 \(B_i\) 表示所有 \(A_i\) 中没有出现过的元素,初始 \(B_i=\mathbb{N}^*\),那么问题变为:
- 区间删除一个元素 \(x\)。
- 查询 \(\displaystyle \max_{l \le i \le r} \operatorname{mex} B_i\)。
再用 set 维护所有极长区间,满足 \(x\) 在这些区间的 \(B_i\) 中出现了,那么 \(x\) 就只能对这个 set 里的区间造成贡献。容易用颜色段均摊做。
计算区间贡献时就在每个线段树节点上维护个 set 即可。
时间复杂度:\(O(n\log n+q\log^2n)\)。
Code
#include <bits/stdc++.h>
// #define int int64_t
using i64 = int64_t;
const int kMaxN = 2e5 + 5;
int n = 2e5, q;
std::set<std::pair<int, int>> seg[kMaxN];
struct SGT {
  int val[kMaxN * 4];
  std::set<int> st[kMaxN * 4];
  void pushup(int x) {
    val[x] = std::min(!st[x].size() ? n : *st[x].begin(), std::max(val[x << 1], val[x << 1 | 1]));
  }
  void build(int x, int l, int r) {
    val[x] = n;
    if (l == r) return;
    int mid = (l + r) >> 1;
    build(x << 1, l, mid), build(x << 1 | 1, mid + 1, r);
  }
  void update(int x, int l, int r, int ql, int qr, int v, bool op = 1) {
    if (l > qr || r < ql) {
      return;
    } else if (l >= ql && r <= qr) {
      if (op) st[x].emplace(v);
      else st[x].erase(v);
      return l == r ? void(val[x] = (!st[x].size() ? n : *st[x].begin())) : pushup(x);
    }
    int mid = (l + r) >> 1;
    update(x << 1, l, mid, ql, qr, v, op), update(x << 1 | 1, mid + 1, r, ql, qr, v, op);
    pushup(x);
  }
  int query(int x, int l, int r, int ql, int qr) {
    if (l > qr || r < ql) return 0;
    else if (l >= ql && r <= qr) return val[x];
    int mid = (l + r) >> 1;
    return std::min(!st[x].size() ? n : *st[x].begin(), std::max(query(x << 1, l, mid, ql, qr), query(x << 1 | 1, mid + 1, r, ql, qr)));
  }
} sgt;
void split(int x, int p) {
  auto it = seg[x].lower_bound({p + 1, 0});
  if (it == seg[x].begin()) return;
  --it;
  auto [l, r] = *it;
  if (l == p || r < p) return;
  sgt.update(1, 0, n, l, r, x, 0), sgt.update(1, 0, n, l, p - 1, x, 1), sgt.update(1, 0, n, p, r, x, 1);
  seg[x].erase(it), seg[x].emplace(l, p - 1), seg[x].emplace(p, r);
}
void ins(int l, int r, int v) {
  split(v, l), split(v, r + 1);
  for (auto it = seg[v].lower_bound({l, 0}); it != seg[v].end() && it->second <= r; it = seg[v].lower_bound({l, 0})) {
    sgt.update(1, 0, n, it->first, it->second, v, 0);
    seg[v].erase(it);
  }
}
int query(int l, int r) { return sgt.query(1, 0, n, l, r); }
void dickdreamer() {
  std::cin >> q;
  sgt.build(1, 0, n);
  for (int i = 0; i <= n; ++i) seg[i].emplace(0, n), sgt.update(1, 0, n, 0, n, i, 1);
  i64 S = 0;
  for (int i = 1; i <= q; ++i) {
    int op;
    i64 v, l, r;
    std::cin >> op;
    if (op == 1) {
      std::cin >> v >> l >> r;
      v ^= S, l ^= S, r ^= S;
      ins(l, r, v);
    } else {
      std::cin >> l >> r;
      l ^= S, r ^= S;
      int res = query(l, r);
      std::cout << res << '\n';
      S += res;
    }
  }
}
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;
}
 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号