P3302 [SDOI2013]森林 主席树+LCA+启发式合并
Posted kaka0010
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3302 [SDOI2013]森林 主席树+LCA+启发式合并相关的知识,希望对你有一定的参考价值。
原题链接:https://www.luogu.com.cn/problem/P3302
题意
一个森林由n个节点m条边组成,满足下列两种操作
- Q x y k 查询x到y路径上权值第k小的点
- L x y 将x和y之间连一条边
注意本题强制在线
分析
看到动态建边操作,很多人会想到LCT,这题应该是可以写的,但我不会 。
其实这题就是P2633 Count on a tree的加强版,多了一个动态建边的过程。在那道题中,求树上路径的第k小点时,左子树的大小可以转化为
s
i
z
[
u
]
+
s
i
z
[
v
]
−
s
i
z
[
l
c
a
]
−
s
i
z
[
f
a
l
c
a
]
siz[u]+siz[v]-siz[lca]-siz[falca]
siz[u]+siz[v]−siz[lca]−siz[falca]
其中falca是lca的父节点,如果lca没有父节点,应该初始化为0。
然后就是加边操作,因为题目已经保证这是一个森林,因此不会成环,我们可以放心用并查集找到根节点,但不能路径压缩,因为我们需要知道整棵树的结构,这时候就可以用到启发式合并了。我们将siz较小的树并到siz较大的树上可以保证时间复杂度在NlogN。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 10;
const int M = 1e5 + 10;
const int MOD = 998244353;
int a[N], b[N], n, m, k, tot, cnt;
int dep[N], f[N][20], vis[N], siz[N], fa[N];
int rt[N];
vector<int> g[N];
int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
struct node {
int ls, rs;
int sum;
}hjt[N * 50];
int Hash(int x) {
return lower_bound(b + 1, b + cnt + 1, x) - b;
}
void modify(int &now, int pre, int l, int r, int pos) {
now = ++tot;
hjt[now] = hjt[pre];
hjt[now].sum = hjt[pre].sum + 1;
if (l == r) return;
int mid = (l + r) >> 1;
if (pos <= mid) modify(hjt[now].ls, hjt[pre].ls, l, mid, pos);
else modify(hjt[now].rs, hjt[pre].rs, mid + 1, r, pos);
}
int query(int u, int v, int lca, int falca, int l, int r, int K) {
if (l == r) return b[l];
int num = hjt[hjt[u].ls].sum + hjt[hjt[v].ls].sum - hjt[hjt[lca].ls].sum - hjt[hjt[falca].ls].sum;
int mid = (l + r) >> 1;
if (K <= num) return query(hjt[u].ls, hjt[v].ls, hjt[lca].ls, hjt[falca].ls, l, mid, K);
else return query(hjt[u].rs, hjt[v].rs, hjt[lca].rs, hjt[falca].rs, mid+1, r, K-num);
}
void dfs(int x, int far, int Rt) {
dep[x] = dep[far] + 1; f[x][0] = far;
for (int i = 1; i <= 17; i++) f[x][i] = f[f[x][i-1]][i-1];
siz[Rt]++; fa[x] = far;
vis[x] = 1;
modify(rt[x], rt[far], 1, cnt, Hash(a[x]));
// cout << x << " " << Hash(a[x]) << endl;
for (auto v : g[x]) {
if (v == far) continue;
dfs(v, x, Rt);
}
}
int lca(int x, int y) {
if (dep[x] > dep[y]) swap(x, y);
for (int i = 17; i >= 0; i--) if (dep[y] - dep[x] >= (1 << i)) y = f[y][i];
if (x == y) return x;
for (int i = 17; i >= 0; i--) {
if (f[y][i] != f[x][i])
y = f[y][i], x = f[x][i];
}
return f[x][0];
}
void solve () {
int T; cin >> T;
T = 1; while (T--) {
cin >> n >> m >> k;
for (int i = 1; i <= n; i++) {
cin >> a[i];
b[i] = a[i];
fa[i] = i;
}
for (int i = 1; i <= m; i++) {
int u, v; cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
sort(b + 1, b + n + 1);
cnt = unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
dfs(i, 0, i);
fa[i] = i;
}
}
int lastans = 0;
for (int i = 1; i <= k; i++) {
char op;
int x, y, rk;
cin >> op;
if (op == 'Q') {
cin >> x >> y >> rk;
x ^= lastans, y ^= lastans, rk ^= lastans;
int _lca = lca(x, y);
int fa_lca = f[_lca][0];
lastans = query(rt[x], rt[y], rt[_lca], rt[fa_lca], 1, cnt, rk);
cout << lastans << endl;
} else {
cin >> x >> y;
x ^= lastans, y ^= lastans;
g[x].push_back(y);
g[y].push_back(x);
int fx = find(x);
int fy = find(y);
if (siz[fx] > siz[fy]) {
swap(fx, fy);
swap(x, y);
}
dfs(x, y, fy);
}
}
}
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#ifdef ACM_LOCAL
freopen("input", "r", stdin);
freopen("output", "w", stdout);
signed test_index_for_debug = 1;
char acm_local_for_debug = 0;
do {
if (acm_local_for_debug == '$') exit(0);
if (test_index_for_debug > 20)
throw runtime_error("Check the stdin!!!");
auto start_clock_for_debug = clock();
solve();
auto end_clock_for_debug = clock();
cout << "Test " << test_index_for_debug << " successful" << endl;
cerr << "Test " << test_index_for_debug++ << " Run Time: "
<< double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl;
cout << "--------------------------------------------------" << endl;
} while (cin >> acm_local_for_debug && cin.putback(acm_local_for_debug));
#else
solve();
#endif
return 0;
}
以上是关于P3302 [SDOI2013]森林 主席树+LCA+启发式合并的主要内容,如果未能解决你的问题,请参考以下文章