bzoj3237 [AHOI2013]连通图

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;
}
posted @ 2019-06-09 09:53  cnJuanzhang  阅读(168)  评论(0编辑  收藏  举报