根号重构学习笔记
简介
根号重构其实就是在时间上分块,但是每一块不一定是根号,具体块长应该看题目。
根号重构的大致套路就是将每 \(B\) 个操作放在一起处理,假设有 \(q\) 次操作,那么我们只需要重构 \(\frac{q}{B}\) 次,由于每块内最多只有 \(B\) 个操作,所以一些暴力在此时时间复杂度就是对的了。
例题
P11369 [Ynoi2024] 弥留之国的爱丽丝
考虑根号重构,每隔 B 次操作进行一次重构。
每次重构,令这次重构到下次重构中间操作过的边叫做关键边,涉及到的点叫做关键点,显然关键点与关键边的数量都是 \(O(B)\) 级别的。每次重构时我们求出不考虑关键边关键点两两之间的连通性,即求出 \(base_{i,j}\) 表示 \(i\) 是否可以通过非关键边到达 \(j\),其中 \(i,j\) 均为关键点。这可以先对原图缩点,然后在反图上跑拓扑求的。具体的,我们应考虑求出 \(f_{i,j}\) 表示第 \(i\) 个 scc 可以到达的关键点有哪些。于是单次重构时间复杂度为 \(O(n+\frac{m\cdot B}{\omega})\)。
每次查询暴力加如块内涉及的 \(O(B)\) 条关键边,然后 bfs 查询连通性即可。但朴素的 bfs 时间复杂度为 \(O(|E|)\),这里 \(|E|=B^2\) 为边数,所以查询的总时间复杂度是 \(O(q \cdot B^2)\),写得好才能通过。但是我们注意到只有 \(O(B)\) 个点,考虑能否使用点数很小的性质优化。有个东西叫做压位 bfs,维护 \(now\) 表示所有未被访问过的点,维护 \(y\) 表示点可以到达的点(是个状压,使用 \(bitset\)),于是 \(now \land y\) 中 \(1\) 的位置就是我们可以扩展到的位置。单次 bfs 时间复杂度 \(O(\frac{B^2}{\omega})\)。
分析一下总体时间复杂度,\(O(\frac{q}{B} \cdot (n+\frac{m \cdot B}{\omega})+q \cdot \frac{B^2}{\omega})=O(\frac{q \cdot n}{B}+\frac{q \cdot m}{\omega}+\frac{q \cdot B^2}{\omega})\)。\(B=220\) 时实测最快。
代码
#include <bits/stdc++.h>
#define eb emplace_back
using namespace std;
char *p1, *p2, buf[1 << 20];
#define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2) ? EOF : *p1++)
template <typename T> void read(T &x) {
x = 0; T f = 1; char c = gc();
while(c < '0' || '9' < c) f = (c == '-') ? -1 : 1, c = gc();
while('0' <= c && c <= '9') x = x * 10 + (c & 15), c = gc();
x *= f;
}
const int maxn = 5e4 + 5, maxm = 1e5 + 5;
const int B = 220, maxb = 445;
int n, m, q, u[maxm], v[maxm];
int key[maxn], deg[maxn], id[maxn];
vector<int> e[maxn];
bool del[maxm], fn[maxn], fe[maxm];
bitset<maxb> f[maxn], g[maxb], base[maxb];
int head[maxn], ecnt;
struct Edge {
int nxt, y;
} edge[maxm];
inline void add(int u, int v) {
edge[++ecnt] = {head[u], v};
head[u] = ecnt;
}
struct operation {
int opt, k, u, v;
} op[maxb];
int tim, tot, dfn[maxn], low[maxn], scc[maxn];
int top, st[maxn];
bool vis[maxn];
void init() {
ecnt = tim = tot = top = 0;
for(int i = 1; i <= n; i++) {
head[i] = deg[i] = fn[i] = 0;
f[i].reset();
e[i].clear();
dfn[i] = low[i] = scc[i] = vis[i] = 0;
}
for(int i = 1; i <= m; i++) fe[i] = 0;
}
void tarjan(int x) {
dfn[x] = low[x] = ++tim;
st[++top] = x, vis[x] = 1;
for(int i = head[x]; i; i = edge[i].nxt) {
int y = edge[i].y;
if(!dfn[y]) {
tarjan(y);
low[x] = min(low[x], low[y]);
}
else if(vis[y]) low[x] = min(low[x], dfn[y]);
}
if(low[x] == dfn[x]) {
tot++;
while(top) {
int t = st[top--];
scc[t] = tot, vis[t] = 0;
if(t == x) break;
}
}
}
void bfs(int s, int t, int cnt) {
bitset<maxb> now;
queue<int> q;
now.set(), q.push(s);
now[s] = 0;
while(!q.empty()) {
int x = q.front(); q.pop();
if(x == t) return (void)(puts("YES"));
bitset<maxb> mask = now & g[x];
while(mask.any()) {
int y = mask._Find_first();
q.push(y);
now[y] = mask[y] = 0;
}
}
puts("NO");
return;
}
int main() {
read(n), read(m), read(q);
for(int i = 1; i <= m; i++) read(u[i]), read(v[i]);
for(int l = 1; l <= q; l += B) {
int r = min(l + B - 1, q), len = r - l + 1;
init();
int cnt = 0;
vector<int> key_edge;
for(int i = 1; i <= len; i++) {
read(op[i].opt);
if(op[i].opt == 1) {
read(op[i].k);
fe[op[i].k] = 1;
int x = u[op[i].k], y = v[op[i].k];
fn[x] = fn[y] = 1;
key[++cnt] = x, key[++cnt] = y;
key_edge.eb(op[i].k);
}
else {
read(op[i].u), read(op[i].v);
key[++cnt] = op[i].u, key[++cnt] = op[i].v;
}
}
sort(key + 1, key + cnt + 1);
cnt = unique(key + 1, key + cnt + 1) - key - 1;
for(int i = 1; i <= cnt; i++) id[key[i]] = i;
for(int i = 1; i <= m; i++) if(!fe[i] && !del[i]) add(u[i], v[i]);
for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i);
for(int i = 1; i <= m; i++) {
if(!fe[i] && !del[i]) {
int x = scc[u[i]], y = scc[v[i]];
if(x != y) e[y].eb(x), deg[x]++;
}
}
for(int i = 1; i <= cnt; i++) f[scc[key[i]]][i] = 1;
queue<int> q;
for(int i = 1; i <= tot; i++) if(!deg[i]) q.push(i);
while(!q.empty()) {
int x = q.front(); q.pop();
for(int y : e[x]) {
f[y] |= f[x];
if(!--deg[y]) q.push(y);
}
}
for(int i = 1; i <= cnt; i++) base[i].reset();
for(int i = 1; i <= cnt; i++) {
for(int j = 1; j < i; j++) {
int u = key[i], v = key[j];
base[i][j] = f[scc[u]][j], base[j][i] = f[scc[v]][i];
}
}
for(int i = 1; i <= len; i++) {
if(op[i].opt == 1) del[op[i].k] ^= 1;
else {
for(int j = 1; j <= cnt; j++) g[j] = base[j];
for(int j : key_edge) if(fe[j] && !del[j]) g[id[u[j]]][id[v[j]]] = 1;
bfs(id[op[i].u], id[op[i].v], cnt);
}
}
}
return 0;
}

浙公网安备 33010602011771号