hdu7171 - Range Reachability Query 题解

现在感觉这题确实挺自然的(

首先不弱于 \(q\) 组询问查询可达性。以前以为这只能 \(\mathrm O\!\left(\dfrac {nm}w\right)\),但仔细想想,你对每个 \(x\) 维护一个 bitset 存是否能到达每个 \(y\),如果 \(y\) 不是询问里的 \(v_i\) 的话那完全没必要记。所以 bitset 里只要存到 \(q\)\(v_i\) 的可达性就行了,复杂度 \(\mathrm O\!\left(\dfrac{qm}w\right)\),空间 \(\mathrm O\!\left(\dfrac{nq}w\right)\)

那这个题肯定复杂度至少是这么多。考虑在原来的基础上魔改,原来 bitset 里维护的是到 \(q\) 个询问的目的地的可达性,容易想到再把 \(l_i,r_i\) 的限制加进去就行了。

这样依然容易转移:枚举边 \((x,y,id)\),哪些询问能产生贡献?一方面要 \(f_{y,i}=\mathrm{true}\),一方面要 \(id\in[l_i,r_i]\)。所以再维护一个 bitset \(g_{id}\) 表示满足 \(id\in[l_i,r_i]\) 的那些 \(i\) 的集合,于是转移有 f[x] |= f[y] & g[id]。预处理 \(g\) 异或差分一下就行了,这样复杂度不变。

但这样空间不太开的下(我没试,但一般 1e5 * 1e5 的 bitset 基本上开不了吧),这里用到一个叫分块 bitset 的技巧。其实之前写过很多次,就是你完全可以把 \(q\) 拆成若干长度为 \(B\) 的份,对每份做一遍。这样如果 \(B\) 足够小,空间就开的下了,而且对时间复杂度几乎没影响——唯一增加的就是原来不是常数瓶颈的遍历邻接表的 \(\mathrm O(m)\),现在要进行 \(\mathrm O\!\left(\dfrac qB\right)\) 次了,所以 \(B\) 不能太小(以及如果 \(B<w\) 还会导致 bitset 优化失效)。事实上 \(B\) 取 128 比较好,这样每份的 bitset 用一个 u128 就能存下(也就是可以认为 \(w=128\)),这样增加的时间也可以认为是 \(\mathrm O\!\left(\dfrac{qm}w\right)\),没变。

为什么要叫分块 bitset 呢?因为并不是任何时候 \(B\)\(w\) 都最好,可能需要稍微调大一点来减小时间常数,所以 \(B\) 可以自己调。

constexpr int N = 5e4 + 10, M = 1e5 + 10;

int n, m, qu;
vii nei[N];
int u[N], v[N], L[N], R[N];

namespace solver {
int u[128], v[128], L[128], R[128];

u128 to[N], in[M];
void solve(int lim) {
  memset(to, 0, sizeof(to)), memset(in, 0, sizeof(in));
  REP(i, 0, 127) {
    to[v[i]] |= (u128)1 << i;
    in[L[i]] ^= (u128)1 << i, in[R[i] + 1] ^= (u128)1 << i;
  }
  REP(i, 1, m) in[i] ^= in[i - 1];
  PER(x, n, 1) {
    for(tii e : nei[x]) {
      int y, id; tie(y, id) = e;
      to[x] |= to[y] & in[id];
    }
  }
  REP(i, 0, lim - 1) {
    puts(to[u[i]] >> i & 1 ? "YES" : "NO");
  }
}
}

void mian() {
  n = read(), m = read(), qu = read();
  REP(i, 1, n) nei[i].clear();
  REP(i, 1, m) {
    int x = read(), y = read();
    nei[x].pb(y, i);
  }
  REP(i, 0, qu - 1) u[i] = read(), v[i] = read(), L[i] = read(), R[i] = read();
  REP(b, 0, (qu - 1) / 128) {
    int l = b * 128, hop = (b + 1) * 128 - 1, r = min(qu - 1, hop);
    int now = 0;
    REP(i, l, r) {
      solver::u[now] = u[min(i, hop)];
      solver::v[now] = v[min(i, hop)];
      solver::L[now] = L[min(i, hop)];
      solver::R[now] = R[min(i, hop)];
      ++now;
    }
    solver::solve(r - l + 1);
  }
}
posted @ 2022-08-09 09:47  ycx060617  阅读(193)  评论(2编辑  收藏  举报