bzoj3237 [AHOI2013]连通图
给定一张无向连通图,有 \(q\) 次询问,每次询问给定一些边 ,求在原图中去掉这些边后,图是否联通
\(n,\ q\leq10^5,\ m\leq2\times10^5\) ,每次询问边数 \(\leq4\)
cdq分治,分治,并查集
实际上这并不能算是严格的 cdq分治……
应该算是最大化询问间重复计算的部分并优化复杂度
考虑按询问时间分治,对于当前区间 \([l,\ r]\) ,在去除了询问的这些边后,用并查集维护连通块
在递归下去一侧前要先消去另一侧的贡献,因此要将另一侧有这一侧没有的边加入并查集,去重可以用带清空的数组实现
而 \([l,\ mid],\ [mid+1,\ r]\) 的并查集可以共享,在处理完 \([l,\ mid]\) 后可以撤销操作重新回到 \([l,\ r]\) 的状态,再处理 \([mid+1,\ r]\) 。由于每次操作只会被撤销一次,因此时间复杂度有保证。(我感觉并查集单次操作是 \(O(\log n)\) 的
时间复杂度 \(O(\displaystyle\sum q\log n\log\displaystyle\sum q)\)
代码(开 C++11)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
bool ans[maxn];
int n, m, q, k;
struct edge {
int u, v;
} e[maxn << 1];
vector <int> Q[maxn];
#define nc getchar()
inline int read() {
int x = 0;
char c = nc;
while (c < 48) c = nc;
while (c > 47) x = (x << 3) + (x << 1) + (c ^ 48), c = nc;
return x;
}
#undef nc
// ---
bool vis[maxn << 1];
int now, T[maxn << 1];
inline void clr() { ++now; }
inline void upd(int pos) {
T[pos] = now, vis[pos] = 1;
}
inline bool query(int pos) {
return T[pos] < now ? 0 : vis[pos];
}
// array with clear
int par[maxn], sz[maxn];
int tot, top, st[maxn], bl[maxn];
inline int find(int x) {
while (par[x] != x) x = par[x];
return x;
}
inline void unite(int x, int y) {
if ((x = find(x)) != (y = find(y))) {
if (sz[x] < sz[y]) swap(x, y);
--tot;
par[y] = x, sz[x] += sz[y];
st[++top] = y, bl[top] = k;
}
}
inline void revert() {
for (; bl[top] >= k; --top, ++tot) {
int u = st[top];
sz[par[u]] -= sz[u], par[u] = u;
}
}
// union set
void cdq(int l, int r) {
if (l == r) {
ans[l] = tot == 1; return;
}
int mid = (l + r) >> 1;
for (int i = l; i <= mid; ++i) {
for (int p : Q[i]) upd(p);
}
for (int i = mid + 1; i <= r; ++i) {
for (int p : Q[i]) {
if (!query(p)) unite(e[p].u, e[p].v);
}
}
clr();
k <<= 1, cdq(l, mid), k >>= 1;
revert();
for (int i = mid + 1; i <= r; ++i) {
for (int p : Q[i]) upd(p);
}
for (int i = l; i <= mid; ++i) {
for (int p : Q[i]) {
if (!query(p)) unite(e[p].u, e[p].v);
}
}
clr();
k = k << 1 | 1, cdq(mid + 1, r), k >>= 1;
revert();
}
int main() {
n = read(), m = read();
for (int i = 1; i <= m; ++i) {
e[i].u = read(), e[i].v = read();
}
q = read();
for (int i = 1; i <= q; ++i) {
int k = read(), x;
while (k--) {
x = read();
Q[i].push_back(x), upd(x);
}
}
tot = n;
for (int i = 1; i <= n; ++i) {
par[i] = i, sz[i] = 1;
}
for (int i = 1; i <= m; ++i) {
if (!query(i)) unite(e[i].u, e[i].v);
}
clr(), k = 1, cdq(1, q);
for (int i = 1; i <= q; ++i) {
puts(ans[i] ? "Connected" : "Disconnected");
}
return 0;
}