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);
}
}