动态图连通性

来源:互联网 发布:灰度分析软件 编辑:程序博客网 时间:2024/05/18 01:27

在LOJ上看到一道模板题,貌似还值得一写的样子。LOJ传送门


你要维护一张无向简单图。你被要求加入删除一条边及查询两个点是否连通。

  • 0:加入一条边。保证它不存在。
  • 1:删除一条边。保证它存在。
  • 2:查询两个点是否联通。

N5000,M500000


这种东西,我当然只会离线的做法。先处理出每条边存在的时间区间,然后将其拆分放在线段树的O(log)个结点上。最后在线段树上DFS,经过一个结点的时候把结点上的边加入图中,同时用可回溯的按秩合并的并查集维护图中点之间的连通性。


据说这就是个动态图的模板题,据说动态图支持O(NlogN)在线修改询问,不知道出题人交的23KB代码是不是这个算法。


C++代码

#include <set>#include <vector>#include <cstdio>#include <cstring>#include <cstdlib>#include <iostream>#include <algorithm>const int mxn = 50005;const int mxm = 1000005;int n, m;struct operation {int k, x, y, t, i;} opt[mxm];struct edge {int x, y, l, r;inline edge(void) {} ;inline edge(int a, int b, int c = 0, int d = 0): x(a), y(b), l(c), r(d) {} ;inline bool operator < (const edge &e) const {if (x != e.x)return x < e.x;if (y != e.y)return y < e.y;if (l != e.l)return l < e.l;}} edg[mxm];std::set<edge> waiting;std::vector<edge> node[mxm << 2];std::vector<operation> query[mxm << 2];void insertEdge(int t, int l, int r, const edge &e) {// insert an edge on segment treeif (l >= e.l && r <= e.r)node[t].push_back(e);else {int mid = (l + r) >> 1;if (e.l <= mid)insertEdge(t << 1, l, mid, e);if (e.r > mid)insertEdge(t << 1 | 1, mid + 1, r, e);}}void insertQuery(int t, int l, int r, const operation &p) {// insert a query on segment treeif (l == r)query[t].push_back(p);else {int mid = (l + r) >> 1;if (p.t <= mid)insertQuery(t << 1, l, mid, p);elseinsertQuery(t << 1 | 1, mid + 1, r, p);}}bool answer[mxm];// union-find-setint fa[mxn];int sz[mxn];inline int find(int u) {// find the root of uwhile (u != fa[u])u = fa[u];return u;}// recover stackint rec[mxn], tot;inline void merge(int a, int b) {a = find(a);b = find(b);if (a == b)return ;if (sz[a] > sz[b])std::swap(a, b);rec[++tot] = a;fa[a] = b;sz[b] = sz[b] + sz[a];}inline void recover(int top) {// recover until tot = topwhile (tot > top) {int u = rec[tot--];int f = fa[u];sz[f] = sz[f] - sz[u];fa[u] = u;}}void dfsOnTree(int t, int l, int r) {int top = tot;// use for recover// insert the edges on this nodefor (int i = 0; i < node[t].size(); ++i)merge(node[t][i].x, node[t][i].y);if (l == r) {// answer the querys on ths nodefor (int i = 0; i < query[t].size(); ++i)answer[query[t][i].i] = (find(query[t][i].x) == find(query[t][i].y));}else {int mid = (l + r) >> 1;// goto the subtreesdfsOnTree(t << 1, l, mid);dfsOnTree(t << 1 | 1, mid + 1, r);}recover(top);// recover to top}signed main() {scanf("%d%d", &n, &m);for (int i = 1; i <= m; ++i)scanf("%d%d%d", &opt[i].k, &opt[i].x, &opt[i].y);int tim = 1;// timeint cnt = 0; // number of edgesfor (int i = 1; i <= m; ++i) {if (opt[i].k != 2)opt[i].t = ++tim;elseopt[i].t = tim, opt[i].i = i;if (opt[i].x > opt[i].y)std::swap(opt[i].x, opt[i].y);if (opt[i].k == 0)// insert an edgewaiting.insert(edge(opt[i].x, opt[i].y, tim));else if (opt[i].k == 1) {// erase an edgestd::set<edge>::iterator it = waiting.lower_bound(edge(opt[i].x, opt[i].y));edg[++cnt] = *it, edg[cnt].r = tim - 1;waiting.erase(it);}}for (std::set<edge>::iterator i = waiting.begin(); i != waiting.end(); ++i)edg[++cnt] = *i, edg[cnt].r = tim;for (int i = 1; i <= cnt; ++i)// build treeinsertEdge(1, 1, tim, edg[i]);for (int i = 1; i <= m; ++i)// build treeif (opt[i].k == 2)insertQuery(1, 1, tim, opt[i]);for (int i = 1; i <= n; ++i)// init union-find-setfa[i] = i, sz[i] = 1; dfsOnTree(1, 1, tim);for (int i = 1; i <= m; ++i)if (opt[i].k == 2)puts(answer[i] ? "Y" : "N");}

UPDATE 2017/7/8

用线段树+并查集的方法AC之后,感觉O(Nlog^2)的时间复杂度跑起来还是十分吃力,于是就用LCT做了一个O(NlogN)的算法。

大概就是预先处理出每条边的删去时间,并以其为权值用LCT维护动态最大生成树,同时按时间顺序处理询问和加边操作。


C++代码

#include <set>#include <cstdio>#include <algorithm>template <class T>inline void myswap(T &a, T &b) {register T c;c = a;a = b;b = c;}template <class T>inline T min(const T &a, const T &b) {return a < b ? a : b;}template <class T>inline T max(const T &a, const T &b) {return a > b ? a : b;}const int mxn = 1000005;const int mxm = 1000005;const int inf = 1000000007;namespace linkCutTree {int fat[mxn];int rev[mxn];int siz[mxn];int val[mxn];int sub[mxn];int son[mxn][2];inline bool isroot(int t) {if (!fat[t])return true;int f = fat[t];if (son[f][0] == t)return false;if (son[f][1] == t)return false;return true;}inline void update(int t) {int s;siz[t] = 1;sub[t] = t;if (s = son[t][0]) {siz[t] = siz[t] + siz[s];if (val[sub[t]] > val[sub[s]])sub[t] = sub[s];}if (s = son[t][1]) {siz[t] = siz[t] + siz[s];if (val[sub[t]] > val[sub[s]])sub[t] = sub[s];}}inline void push(int t) {if (rev[t]) {rev[t] = 0;myswap(son[t][0], son[t][1]);if (son[t][0])rev[son[t][0]] ^= 1;if (son[t][1])rev[son[t][1]] ^= 1;}}int stk[mxn], top;inline void pushdown(int t) {stk[top = 1] = t;while (!isroot(t))stk[++top] = t = fat[t];while (top)push(stk[top--]);}inline void rotate(int t) {int f = fat[t];int g = fat[f];int a = son[f][1] == t;int b = !a, s = son[t][b];fat[t] = g;fat[f] = t;son[t][b] = f;son[f][a] = s;if (s)fat[s] = f;if (g) {if (son[g][0] == f)son[g][0] = t;if (son[g][1] == f)son[g][1] = t;}update(f);update(t);}inline void splay(int t) {pushdown(t);while (!isroot(t)) {int f = fat[t];int g = fat[f];if (isroot(f))rotate(t);else {int a = son[f][1] == t;int b = son[g][1] == f;if (a == b)rotate(f), rotate(t);elserotate(t), rotate(t);}}}inline void access(int t) {for (int p = t, q = 0; p; q = p, p = fat[p])splay(p), son[p][1] = q, update(p);splay(t);}inline void mkroot(int t) {access(t), rev[t] ^= 1;}inline void link(int a, int b) {mkroot(a);mkroot(b);fat[a] = b;}inline void cut(int a, int b) {mkroot(a);access(b);fat[a] = 0;son[b][0] = 0;update(b);}}int n, m;struct operation {int k, x, y;} opt[mxm];struct edge {int x, y, l, r;inline edge(void) {} ;inline edge(int a, int b, int t = 0): x(a), y(b), l(t), r(0) {} ;inline bool operator < (const edge &e) const {if (x != e.x)return x < e.x;if (y != e.y)return y < e.y;if (l != e.l)return l < e.l;}} edg[mxm]; inline bool cmp(const edge &a, const edge &b) {return a.l < b.l;}std::set<edge> waiting;int fa[mxn];// union-find-setint find(int u) {return fa[u] == u ? u : fa[u] = find(fa[u]);}signed main() {scanf("%d%d", &n, &m);for (int i = 1; i <= m; ++i)scanf("%d%d%d", &opt[i].k, &opt[i].x, &opt[i].y);int cnt = 0;for (int i = 1; i <= m; ++i) {if (opt[i].x > opt[i].y)myswap(opt[i].x, opt[i].y);if (opt[i].k == 0)waiting.insert(edge(opt[i].x, opt[i].y, i));else if (opt[i].k == 1) {std::set<edge>::iterator it = waiting.lower_bound(edge(opt[i].x, opt[i].y));edg[++cnt] = *it, edg[cnt].r = i;waiting.erase(it);}}for (std::set<edge>::iterator i = waiting.begin(); i != waiting.end(); ++i)edg[++cnt] = *i, edg[cnt].r = m + 1;std::sort(edg + 1, edg + cnt + 1, cmp);using namespace linkCutTree;for (int i = 1; i <= n; ++i) {fa[i] = i;siz[i] = 1;sub[i] = i;val[i] = inf;}int now = 1;for (int i = 1; i <= m; ++i)if (opt[i].k == 2) {while (now <= cnt && edg[now].l <= i) {// insert edgeint x = edg[now].x;int y = edg[now].y;int r = edg[now].r;int fx = find(x);int fy = find(y);if (fx == fy) {// already connectedmkroot(x);access(y);int id = sub[y] - n;int vl = val[sub[y]];if (vl > edg[now].r) {++now; continue; }int a = edg[id].x;int b = edg[id].y;cut(id + n, a);cut(id + n, b);}int id = n + now;sub[id] = id;val[id] = edg[now++].r;link(id, x);link(id, y);if (fx != fy)fa[fx] = fy;}int x = opt[i].x;int y = opt[i].y;int fx = find(x);int fy = find(y);if (fx != fy)puts("N");else {mkroot(x);access(y);int vl = val[sub[y]];if (vl > i)puts("Y");elseputs("N");}}}