HDU 7171 - Range Reachability Query(分块 bitset)
作者:互联网
一道感觉思路挺自然的题,不知道为什么赛时只有三个队过(?)
首先这题肯定严格强于有向图任意两点连通性对吧,所以此题 std 时间复杂度肯定不低于有向图任意两点连通性的复杂度,即 \(\dfrac{nm}{\omega}\),而此题 \(5\times 10^4\) 的数据范围肯定 \(n^2\) 不可能过,因此 bitset 肯定跑不掉的,关键在于如何 bitset。
一个很自然的想法是将点的可达性看成一个个布尔型变量,从这个角度出发你可以得到 \(114514191981019260817998244353\) 种乱搞,赛时我和 ymx 一直在倒腾这些乱搞,但事实证明没有一个可以过。
正确的压位方式:设 \(f_{i,j}\) 表示从 \(i\) 开始,只经过 \([l_j,r_j]\) 之内的边能否到达 \(v_j\),再设 \(g_{i,j}\) 表示是否有 \(i\in[l_j,r_j]\),注意到 \(f_{i,j},g_{i,j}\) 都只有 \(0/1\) 两种取值,因此可以 01 压位。得到的正确状态设计后,转移就很显然了:\(g_{i,j}\) 可以通过差分得到,求 \(f_{i,j}\) 可以拓扑排序,然后对于每条边 \((u,v,id)\),令 \(f_u\) 为 \(f_u|(f_v\&g_{id})\)。
但是直接这么转移还有一个问题就是空间不太能开得下。\(f_{i,j}\) 空间是 \(\dfrac{nq}{\omega}\) 的,问题不大,但是 \(g_{i,j}\) 空间是 \(\dfrac{mq}{\omega}\) 的,空间常数有亿点点大,这里有一个很套路的方法:把差分的过程看作一个时间轴,然后分块,每 \(B\) 个时刻记录一次每个点的状态,这样如果你需要知道一个 \(g_{i,j}\) 的值,你就从上一次记录的位置开始,每遇到一个修改操作就改下这个位置上的值。这样空间复杂度降到了 \((nq+\dfrac{mq}{B})·\dfrac{1}{\omega}\),时间复杂度 \(\dfrac{nq}{\omega}+qB\),毛估估 \(B=\sqrt{q}\) 最优,但大概 \(B=100\) 就够了。
const int MAXN = 5e4;
const int MAXM = 1e5;
const int BLK = 100;
int n, m, qu, lim, hd[MAXN + 5], to[MAXM + 5], nxt[MAXM + 5], ec = 0;
void adde(int u, int v) {to[++ec] = v; nxt[ec] = hd[u]; hd[u] = ec;}
struct qry {int u, v, l, r;} q[MAXN + 5];
pii eve[MAXN * 2 + 5]; int qn, pos[MAXM + 5];
u64 f[MAXN + 5][MAXN / 64 + 5], cur[MAXN / 64 + 5], mem[MAXN * 2 / BLK + 5][MAXN / 64 + 5];
void clear() {
for (int i = 1; i <= n; i++) for (int j = 0; j < lim; j++) f[i][j] = 0;
for (int i = 0; i < lim; i++) cur[i] = 0; ec = 0; qn = 0;
for (int i = 1; i <= n; i++) hd[i] = 0;
}
void solve() {
scanf("%d%d%d", &n, &m, &qu); lim = (qu - 1) / 64 + 1; clear();
for (int i = 1, u, v; i <= m; i++) scanf("%d%d", &u, &v), adde(u, v);
for (int i = 0; i < qu; i++) {
scanf("%d%d%d%d", &q[i].u, &q[i].v, &q[i].l, &q[i].r);
eve[++qn] = mp(q[i].l, i); eve[++qn] = mp(q[i].r + 1, i);
f[q[i].v][i >> 6] ^= (1ull << (i & 63));
}
sort(eve + 1, eve + qn + 1);
for (int i = 1; i <= qn; i++) {
cur[eve[i].se >> 6] ^= (1ull << (eve[i].se & 63));
if (i % BLK == 0) {
for (int j = 0; j < lim; j++)
mem[i / BLK][j] = cur[j];
}
}
for (int i = 1, j = 1; i <= m; i++) {
while (j <= qn && eve[j].fi <= i) ++j;
pos[i] = j - 1;
}
for (int i = n; i; i--) for (int e = hd[i]; e; e = nxt[e]) {
int x = pos[e]; static u64 tmp[MAXN / 64 + 5];
for (int j = 0; j < lim; j++) tmp[j] = mem[x / BLK][j];
for (int j = x / BLK * BLK + 1; j <= x; j++) tmp[eve[j].se >> 6] ^= (1ull << (eve[j].se & 63));
for (int j = 0; j < lim; j++) f[i][j] |= (f[to[e]][j] & tmp[j]);
}
for (int i = 0; i < qu; i++) printf("%s\n", (f[q[i].u][i >> 6] >> (i & 63) & 1) ? "YES" : "NO");
}
int main() {
int qu; scanf("%d", &qu);
while (qu--) solve();
return 0;
}
标签:HDU,qu,int,dfrac,复杂度,Reachability,Range,MAXN,omega 来源: https://www.cnblogs.com/ET2006/p/HDU-7171.html