洛谷 P4197 Peaks
原题链接
tag :kruskal重构树 + 树上倍增 + 主席树
学习kruskal重构树推荐看这篇
Kruskal重构树入门 - 自为风月马前卒
其实重构树的核心代码很短:
rep (i, 1, m) {
int pu = find(EG[i].u), pv = find(EG[i].v);
if (pu == pv) continue;
now++;
fa[now] = fa[pu] = fa[pv] = now;
h[now] = EG[i].w;
add(now, pu), add(now, pv);
}
就是把将要合并两点的连边断开,新增一个点分别连向这两点,新增点的点权即为断开的边的边权
有很多优秀的性质:
1、是一棵二叉树
2、按照最小生成树建立,则是大根堆
3、任意两点之间的边权最大值为它们的lca
#include<bits/stdc++.h>
using namespace std;
#define fr first
#define se second
#define et0 exit(0);
#define rep(i, a, b) for(int i = (int)(a); i <= (int)(b); i ++)
#define rrep(i, a, b) for(int i = (int)(a); i >= (int)(b); i --)
#define IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
typedef unsigned long long ULL;
const int INF = 0X3f3f3f3f, N = 1e5 + 10, M = 5e5 + 10, MOD = 1e9 + 7;
const double eps = 1e-7, pi = acos(-1);
int n, m, Q;
int now;
int h[N << 1], fa[N << 1];
int lg[N << 1], Fa[N << 1][30], dep[N << 1];
int id, leaf_id[N];
PII rng[N << 1];
vector<int> vc;
struct Egde {
int u, v;
int w;
} EG[M];
int head[N << 1], idx;
struct EGDE {
int to, next;
} eg[M << 1];
void add(int x, int y) {
eg[idx].to = y;
eg[idx].next = head[x];
head[x] = idx++;
}
bool cmp(Egde x, Egde y) {
return x.w < y.w;
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
int find_vc(int x) {
x = -x;
return lower_bound(vc.begin(), vc.end(), x) - vc.begin();
}
void Init_lg() { // lg预处理
for (int i = 1; i <= N; i++) lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);
}
int dfs(int u, int f) {
bool is_leaf = true;
Fa[u][0] = f;
dep[u] = dep[f] + 1;
for (int i = 1; i <= lg[dep[u]]; i++) Fa[u][i] = Fa[Fa[u][i - 1]][i - 1];
for (int i = head[u]; ~i; i = eg[i].next) {
int to = eg[i].to;
if (to == f) continue;
is_leaf = false;
int t = dfs(to, u);
if (t) {
if (!rng[u].fr) rng[u].fr = t;
else rng[u].se = t;
}
else {
if (!rng[u].fr) rng[u].fr = rng[to].fr;
else rng[u].se = rng[to].se;
}
}
if (is_leaf) {
leaf_id[++id] = u;
vc.push_back(-h[u]);
return id;
} else return 0;
}
int find_h(int u, int x) { //倍增
u = Fa[u][0];
if (h[u] > x) return -1;
rrep (j, 18, 0)
if (Fa[u][j] && h[Fa[u][j]] <= x) u = Fa[u][j];
return u;
}
struct NODE {
int l, r;
int cnt;
} tr[N * 4 + N * 21];
int root[N];
int build(int l, int r) {
int q = ++idx;
if (l == r) return q;
else {
int mid = l + r >> 1;
tr[q].l = build(l, mid);
tr[q].r = build(mid + 1, r);
return q;
}
}
int insert(int p, int l, int r, int x) {
int q = ++idx;
tr[q] = tr[p];
if (l == r) {
tr[q].cnt++;
return q;
} else {
int mid = l + r >> 1;
if (x <= mid) tr[q].l = insert(tr[p].l, l, mid, x);
else tr[q].r = insert(tr[p].r, mid + 1, r, x);
tr[q].cnt = tr[tr[q].l].cnt + tr[tr[q].r].cnt;
return q;
}
}
int query(int p, int q, int l, int r, int k) {
if (l == r) return l;
else {
int mid = l + r >> 1;
int cnt = tr[tr[q].l].cnt - tr[tr[p].l].cnt;
if (cnt >= k) return query(tr[p].l, tr[q].l, l, mid, k);
else return query(tr[p].r, tr[q].r, mid + 1, r, k - cnt);
}
}
void work() {
Init_lg();
memset(head, -1, sizeof head);
scanf("%d%d%d", &n, &m, &Q);
rep (i, 1, n) scanf("%d", &h[i]);
rep (i, 1, m) scanf("%d%d%d", &EG[i].u, &EG[i].v, &EG[i].w);
sort(EG + 1, EG + 1 + m, cmp);
//kruskal重构树
now = n;
rep (i, 1, n) fa[i] = i;
rep (i, 1, m) {
int pu = find(EG[i].u), pv = find(EG[i].v);
if (pu == pv) continue;
now++;
fa[now] = fa[pu] = fa[pv] = now;
h[now] = EG[i].w;
add(now, pu), add(now, pv);
}
dfs(now, 0);
sort(vc.begin(), vc.end());
vc.erase(unique(vc.begin(), vc.end()), vc.end());
int vs = vc.size();
idx = 0, root[0] = build(0, vs - 1);
rep (i, 1, n) root[i] = insert(root[i - 1], 0, vs - 1, find_vc(h[leaf_id[i]]));
while (Q--) {
int u, x, k;
scanf("%d%d%d", &u, &x, &k);
int v = find_h(u, x);
if (v == -1 || rng[v].se - rng[v].fr + 1 < k) cout << -1 << endl;
else printf("%d\n", -vc[query(root[rng[v].fr - 1], root[rng[v].se], 0, vs - 1, k)]);
}
}
signed main() {
// IO
int test = 1;
// cin >> test;
while (test--) {
work();
}
return 0;
}