【题目链接】
【思路要点】
- 问题等价于从起点出发只经过 L , L + 1 , L + 2 , . . . , N L,L+1,L+2,...,N L,L+1,L+2,...,N 能够到达的点和终点出发只经过 1 , 2 , 3 , . . . , R 1,2,3,...,R 1,2,3,...,R 能够到达的点是否有交。
- 建出原图的最小/最大生成树的 $Kruskal $ 重构树,这两个点集对应了两棵树上的某两个子树。
- 对于点集根据最小生成树的 $Kruskal $ 重构树的 D F S DFS DFS 序进行重标号,离线询问,在最大生成树的 $Kruskal $ 重构树上线段树合并,即可查询子树内是否存在标号在某一个区间中的点,从而判断两个点集是否有交。
- 时间复杂度 O ( N L o g N ) O(NLogN) O(NLogN) 。
【代码】
#include "werewolf.h" #include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; const int MAXLOG = 20; const int MAXP = 1e7 + 5; struct SegmentTree { struct Node { int lc, rc; int sum; bool leaf; } a[MAXP]; int size, n; void init(int x) { n = x; size = 0; } void update(int root) { a[root].sum = a[a[root].lc].sum + a[a[root].rc].sum; } void modify(int &root, int l, int r, int pos) { if (root == 0) root = ++size; if (l == r) { a[root].sum = 1; a[root].leaf = true; return; } int mid = (l + r) / 2; if (mid >= pos) modify(a[root].lc, l, mid, pos); else modify(a[root].rc, mid + 1, r, pos); update(root); } void modify(int &root, int val) { return modify(root, 1, n, val); } int merge(int x, int y) { if (x == 0 || y == 0) return x + y; if (a[x].leaf) { a[x].sum += a[y].sum; return x; } a[x].lc = merge(a[x].lc, a[y].lc); a[x].rc = merge(a[x].rc, a[y].rc); update(x); return x; } void join(int &to, int from) { to = merge(to, from); } int query(int root, int l, int r, int ql, int qr) { if (root == 0) return 0; if (l == ql && r == qr) return a[root].sum; int mid = (l + r) / 2, ans = 0; if (mid >= ql) ans += query(a[root].lc, l, mid, ql, min(mid, qr)); if (mid + 1 <= qr) ans += query(a[root].rc, mid + 1, r, max(mid + 1, ql), qr); return ans; } int query(int root, int l, int r) { return query(root, 1, n, l, r); } } ST; vector <int> ans, a[MAXN], b[MAXN], c[MAXN]; vector <int> home[MAXN], ql[MAXN], qr[MAXN]; //a : graph, b : tree1, c : tree2. int n, m, q, f[MAXN], root[MAXN]; int fatherb[MAXN][MAXLOG]; int fatherc[MAXN][MAXLOG]; int timer, dfn[MAXN], rit[MAXN]; int F(int x) { if (f[x] == x) return x; else return f[x] = F(f[x]); } void dfs(int pos, int fa) { fatherb[pos][0] = fa; for (int i = 1; i < MAXLOG; i++) fatherb[pos][i] = fatherb[fatherb[pos][i - 1]][i - 1]; dfn[pos] = ++timer; for (unsigned i = 0; i < b[pos].size(); i++) dfs(b[pos][i], pos); rit[pos] = timer; } void work(int pos) { ST.modify(root[pos], dfn[pos]); for (unsigned i = 0; i < c[pos].size(); i++) { work(c[pos][i]); ST.join(root[pos], root[c[pos][i]]); } for (unsigned i = 0; i < home[pos].size(); i++) if (ST.query(root[pos], ql[pos][i], qr[pos][i]) != 0) ans[home[pos][i]] = 1; } vector <int> check_validity(int tn, vector <int> x, vector <int> y, vector <int> s, vector <int> t, vector <int> l, vector <int> r) { n = tn, m = x.size(), q = s.size(); ans.resize(q); for (int i = 0; i <= m - 1; i++) { x[i]++, y[i]++; a[x[i]].push_back(y[i]); a[y[i]].push_back(x[i]); } for (int i = 1; i <= n; i++) f[i] = i; for (int i = 1; i <= n; i++) { for (unsigned j = 0; j < a[i].size(); j++) if (a[i][j] <= i && F(a[i][j]) != i) { b[i].push_back(F(a[i][j])); f[F(a[i][j])] = i; } } dfs(n, 0); for (int i = 1; i <= n; i++) f[i] = i; for (int i = n; i >= 1; i--) { for (unsigned j = 0; j < a[i].size(); j++) if (a[i][j] >= i && F(a[i][j]) != i) { c[i].push_back(F(a[i][j])); fatherc[F(a[i][j])][0] = i; f[F(a[i][j])] = i; } } for (int i = 1; i <= n; i++) for (int j = 1; j < MAXLOG; j++) fatherc[i][j] = fatherc[fatherc[i][j - 1]][j - 1]; for (int i = 0; i <= q - 1; i++) { s[i]++, t[i]++; l[i]++, r[i]++; if (s[i] < l[i] || t[i] > r[i]) continue; int pos = t[i]; for (int j = MAXLOG - 1; j >= 0; j--) if (fatherb[pos][j] != 0 && fatherb[pos][j] <= r[i]) pos = fatherb[pos][j]; int tl = dfn[pos], tr = rit[pos]; pos = s[i]; for (int j = MAXLOG - 1; j >= 0; j--) if (fatherc[pos][j] != 0 && fatherc[pos][j] >= l[i]) pos = fatherc[pos][j]; home[pos].push_back(i); ql[pos].push_back(tl); qr[pos].push_back(tr); } ST.init(n); work(1); return ans; }