题目: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; }
  |