根号重构学习笔记

简介

根号重构其实就是在时间上分块,但是每一块不一定是根号,具体块长应该看题目。

根号重构的大致套路就是将每 \(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;
}
posted @ 2026-10-13 20:47  PrinceEvan  阅读(10)  评论(0)    收藏  举报