洛谷 P3224 永無鄉

maniubi發表於2024-09-03

洛谷 P3224 永無鄉

題意

給出 \(n\) 個點。有兩種操作:

  1. 在點 \(x\) 和點 \(y\) 之間連一條邊。
  2. 詢問與 \(x\) 聯通的點中點權第 \(k\) 小的點。

思路

使用並查集維護連通性。每個聯通塊用一棵平衡樹維護點權,合併時啟發式合併。

時間複雜度:\(O(n \log^2n)\)

程式碼

#include <bits/stdc++.h>
using namespace std;
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
const int N = 1e5 + 5;
__gnu_pbds::tree<int, __gnu_pbds::null_type, less<int>,
__gnu_pbds::rb_tree_tag,
__gnu_pbds::tree_order_statistics_node_update> S[N];
int n, m;
int p[N], q[N], fa[N], Q;
int find(int x) {
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void merge(int x, int y) {
	int fx = find(x), fy = find(y);
	if (fx == fy) return ;
	if (S[fx].size() > S[fy].size()) 
		swap(fx, fy);
	for (auto num : S[fx]) 
		S[fy].insert(num);
	S[fx].clear();
	fa[fx] = fy;
}
int query(int x, int y) {
	int id = find(x);
	if (S[id].size() < y) return -1;
	return q[*S[id].find_by_order(y - 1)];
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin >> n >> m; 
	for (int i = 1; i <= n; i ++) {
		cin >> p[i];
		q[p[i]] = i, fa[i] = i;
		S[i].insert(p[i]);
	}
	for (int i = 1, u, v; i <= m; i ++) {
		cin >> u >> v;
		merge(u, v);
	}
	cin >> Q;
	string op; int x, y;
	while (Q --) {
		cin >> op >> x >> y;
		if (op == "B") {
			merge(x, y);
		} else cout << query(x, y) << "\n";
	}
	return 0;
}