loj 4823 「联合省选 2025」追忆

题目传送门

  传送门

solution 1

  首先处理这个可达性只能通过 bitset。于是我们有一个 1G 的数组了。

  然后一个容易想到的思路是,我们去维护查询 $a_i \in [l, r]$ 中的点有哪些。然后将它和后继节点的 bitset 求交,假设这个集合为 $S$。现在变成求一个 $x$ 使得满足 $b_i \geqslant x$ 那些点的集合 $T_x$ 与 $S$ 的交集非空。

  由于需要支持单点修改,前缀查询,容易想到用树状数组。但是这样空间复杂度和时间复杂度都很高。问题主要出在我们并不太希望有太多的信息上传。因此我们直接考虑分块。直接对块间的每个后缀维护 bitset。假设块大小为 $B$,那么修改的时间复杂度为 $O(n/B)$,查询的复杂度为 $O(\frac{n}{\omega} +B)$

  查询的时候类似,先在块间二分,然后单块的时候暴力即可。时间复杂度 $O(\frac{nq}{\omega}\log \frac{n}{B} + qB)$。$B$ 取大一些就可以在各大 OJ 上通过了。

solution 2

  如果希望去掉 $\log$,那么我们需要去掉二分。那么我们需要枚举 $b$ 这一维。此时我们需要考虑哪些询问已经被满足了。但是这会带来时间轴上的问题:需要考虑不断产生的修改操作。

  考虑对时间轴分块。每 $D$ 个操作分为一块。那么考虑变成了两部分:在块内受到影响的 $i$ 和未受到影响的 $i$。对于受到影响的我们直接暴力更新答案即可。这一部分总时间复杂度为 $O(qD)$。对于未受到影响的,我们要去按 $b$ 从大到小枚举 $i$,我们希望判断对于一个 $i$ 在哪些询问中是合法的。直接把这 $q$ 个询问对应的 mask 矩阵转置过来比较困难。我们考虑对两个条件分别为每个点求对应合法 $q$ 的属性。

  • 对于后继限制,我们沿着拓扑序然后压位扫一遍即可,时间复杂度 $O(\frac{mD}{\omega})$。
  • 对于 $a$ 在 $[l, r]$ 的限制,可以看作每个 $q$ 对区间加上 $2^{i}$。异或差分一下。总时间复杂度 $O(\frac{nD}{\omega})$

  之后从大到小枚举 $b$,对于首次合法的询问记录答案即可。时间复杂度也是 $O(\frac{nD}{\omega})$。

  总共有 $O(\frac{q}{D})$ 块,总时间复杂度是 $O(\frac{nq}{\omega})$。为了实现方便 $D$ 取 64 即可。

Code

  写的是 solution 1

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 128;

// const int N = 1e4 + 16;

const int chunk_base = 12;  // chunk_size = 2048

int T;
int n, m, q;
bitset<N> f[N];
vector<int> G[N];

class SuffixMaintainer {
  public:
    int cnt;
    int p[N], *q;     // p[a_i] = i, q = a
    bitset<N> chk[(N >> chunk_base) + 2];

    void build(int* a) {
      q = a;
      cnt = (n + (1 << chunk_base) - 1) >> chunk_base;
      for (int i = 0; i < n; i++) {
        p[q[i]] = i; 
      }
      for (int i = 0; i < n; i++) {
        chk[i >> chunk_base].set(p[i]);
      }
      for (int i = cnt - 1; ~i; i--) {
        chk[i] |= chk[i + 1];
      }
    }
    
    // >= l
    bitset<N> query(int l) {
      int cid = l >> chunk_base;
      int R = min((cid + 1) << chunk_base, n);
      auto res = chk[cid + 1];
      for (int i = l; i < R; i++) {
        res.set(p[i]);
      }
      return res;
    }
    
    // erase a[x] = y
    void remove(int x) {
      int y = q[x];
      for (int i = y >> chunk_base; ~i; i--) {
        chk[i].reset(x);
      }
      q[x] = -1;
      p[y] = -1;
    }

    // update a[x] = y
    void update(int x, int y) {
      q[x] = y;
      p[y] = x;
      for (int i = y >> chunk_base; ~i; i--) {
        chk[i].set(x);
      }
    }

    void clear() {
      for (int i = 0; i < cnt; i++) {
        chk[i].reset();
      }
    }
};

int a[N], b[N];
SuffixMaintainer A, B;

void solve() {
  scanf("%d%d%d", &n, &m, &q);
  for (int i = 0, u, v; i < m; i++) {
    scanf("%d%d", &u, &v);
    --u, --v;
    G[u].push_back(v);
  }
  for (int i = 0; i < n; i++) {
    scanf("%d", a + i);
    --a[i];
  }
  for (int i = 0; i < n; i++) {
    scanf("%d", b + i);
    --b[i];
  }
  for (int i = n - 1; ~i; i--) {
    f[i].set(i);
    for (auto e : G[i]) {
      f[i] |= f[e];
    }
  }
  A.build(a);
  B.build(b);
  int op, x, l, r;
  while (q--) {
    scanf("%d%d%d", &op, &x, &l);
    --x, --l;
    if (op == 1) {
      if (x == l) {
        continue;
      }
      int vx = a[x], vy = a[l];
      A.remove(x);
      A.remove(l);
      A.update(x, vy);
      A.update(l, vx);
    } else if (op == 2) {
      if (x == l) {
        continue;
      }
      int vx = b[x], vy = b[l];
      B.remove(x);
      B.remove(l);
      B.update(x, vy);
      B.update(l, vx);
    } else {
      scanf("%d", &r);
      --r;
      auto msk = f[x] & (A.query(l) ^ A.query(r + 1));
      int L = 0, R = B.cnt - 1;
      while (L <= R) {
        int mid = (L + R) >> 1;
        if ((B.chk[mid] & msk).any()) {
          L = mid + 1;
        } else {
          R = mid - 1;
        }
      }
      int p = min(L << chunk_base, n) - 1;
      while (~p) {
        if (msk.test(B.p[p])) {
          break;;
        }
        p--;
      }
      printf("%d\n", p + 1);
    }
  }
}

void clear() {
  for (int i = 0; i < n; i++) {
    G[i].clear();
  }
  for (int i = 0; i < n; i++) {
    f[i].reset();
  }
  A.clear();
  B.clear();
}

int main() {
  freopen("recall.in", "r", stdin);
  freopen("recall.out", "w", stdout);
  scanf("%*d%d", &T);
  while (T--) {
    solve();
    clear();
  }
  return 0;
}
posted @ 2025-03-09 15:23  阿波罗2003  阅读(24)  评论(0)    收藏  举报