题目:CF466E
洛谷链接
CF 链接
模拟赛把 T1 跳了来写这个(T3),离线思路很简单,比 T1 思维题好想。
首先,最终状态是若干颗上下级关系的树,所以我们可以将整个问题放在最后建好的树树上考虑。
对于操作一,将 $x$ 与 $y$ 连边即可。
对于操作二,可以用并查集优化复杂度快速找出当前的最高上司。
对于操作三,我们在每次操作二时把上下两个节点 $(u_p, u_d)$ 储存下来,判断节点 $x$ 是否在路径 $i$ 上可以使用最近公共祖先(LCA),若 $\text{LCA}(u_p, x) = u_p \land \text{LCA}(x, u_d) = x$,则 $x$ 在路径上,反之则不在。
需要注意的是,最终状态不一定是一棵树,可能有多棵树,在 LCA 初始化时要处理一下。
倍增写 LCA 的话时间复杂度为 $O(n \log n)$。
赛时代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| namespace dsu { int fa[N]; void init(int n) { for(int i = 1; i <= n; i++) fa[i] = i; } int find(int u) { if(fa[u] == u) return u; fa[u] = find(fa[u]); return fa[u]; } void connect(int u, int v) { fa[u] = v; } } typedef pair<int, int> pii; vector <int> G[N]; int n, m; vector <pii> ask; pii cov[N]; int tp = 0; int root; int st[N][20]; int dep[N]; bool vis[N]; void dfs(int u, int f, int d) { vis[u] = 1; st[u][0] = f; dep[u] = d; for(int i = 1; i <= 19; i++) st[u][i] = st[ st[u][i - 1] ][i - 1]; for(auto v : G[u]) { if(v == f) continue; dfs(v, u, d + 1); } return; } int lca(int u, int v) { if(dep[u] < dep[v]) swap(u, v); if(u == v) return u; for(int i = 19; i >= 0; i--) { if(dep[st[u][i]] >= dep[v]) u = st[u][i]; } if(u == v) return u; for(int i = 19; i >= 0; i--) { if(st[u][i] != st[v][i]) u = st[u][i], v = st[v][i]; } return st[u][0]; } vector <int> rootl; int32_t main() { n = read(), m = read(); dsu::init(n); while(m--) { int opt = read(); if(opt == -1) break; if(opt == 1) { int u = read(), v = read(); dsu::connect(u, v); G[u].push_back(v); G[v].push_back(u); rootl.push_back(v); } else if(opt == 2) { int u = read(); cov[++tp] = {u, dsu::find(u)}; } else { int u = read(), v = read(); ask.push_back({u, v}); } } for(int i = 1; i <= n; i++) { int root = dsu::find(i); if(!vis[root]) dfs(root, root, 1); } for(auto [x, i]:ask) { int upp = cov[i].second, udd = cov[i].first; if(dsu::find(x) != dsu::find(upp)) cout << "NO" << endl; else if(x == upp || x == udd) cout << "YES" << endl; else cout << (lca(upp, x) == upp && lca(udd, x) == x ? "YES" : "NO") << endl; } return 0; }
|