【BZOJ3551】[ONTAK2010]Peaks加强版
题面
给你一个图,每次询问给定一个位置、长度和\(k\),问从这个点出发,只能经过不大于这个长度的边,到达的点中点权第\(k\)大的点权
图的规模:\(10^5\)
题解
考虑离线怎么做:
将所有询问存下来,按照边权排序
每次加边线段树合并查\(kth\)即可
然鹅:离线加边一时爽,强制在线火葬场。
我们考虑强制在线怎么做:
妈妈!!我会\(Kruskal\)重构树!!!
我们有\(Kruskal\)重构树(这里是\(MST\))的性质:深度越深,节点权值越小
可以二分一下最浅的权值小于等于\(k\)的点,那么直接在它子树内用主席树\(kth\)就行了
辣鸡bzoj,这题空间开得神他妈玄学,稍微变一点就会挂
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
inline int gi() {
register int data = 0, w = 1;
register char ch = 0;
while (!isdigit(ch) && ch != '-') ch = getchar();
if (ch == '-') w = -1, ch = getchar();
while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar();
return w * data;
}
const int MAX_N = 2e5 + 5;
const int MAX_LOG_N = 17;
struct Node { int ls, rs, v; } t[MAX_N * 25];
int tot, rt[MAX_N << 1];
void insert(int &o, int p, int l, int r, int pos) {
o = ++tot, t[o] = t[p], ++t[o].v;
if (l == r) return ;
int mid = (l + r) >> 1;
if (pos <= mid) insert(t[o].ls, t[p].ls, l, mid, pos);
else insert(t[o].rs, t[p].rs, mid + 1, r, pos);
}
int query(int v, int u, int l, int r, int k) {
if (l == r) return l;
int mid = (l + r) >> 1, res = t[t[u].ls].v - t[t[v].ls].v;
if (res >= k) return query(t[v].ls, t[u].ls, l, mid, k);
else return query(t[v].rs, t[u].rs, mid + 1, r, k - res);
}
struct Graph { int to, next; } e[MAX_N << 1]; int fir[MAX_N], e_cnt;
void clearGraph() { memset(fir, -1, sizeof(fir)); e_cnt = 0; }
void Add_Edge(int u, int v) { e[e_cnt] = (Graph){v, fir[u]}, fir[u] = e_cnt++; }
struct Edge { int u, v, w; } a[MAX_N << 2];
bool operator < (const Edge &l, const Edge &r) { return l.w < r.w; }
int N, M, Q, h[MAX_N];
int pa[MAX_N], val[MAX_N];
int getf(int x) { return (x == pa[x]) ? x : (pa[x] = getf(pa[x])); }
int fa[MAX_N][MAX_LOG_N], mx[MAX_N][MAX_LOG_N], p[MAX_N << 1], dep[MAX_N], top;
int st[MAX_N], ed[MAX_N];
bool vis[MAX_N];
void dfs(int x) {
vis[x] = 1, p[++top] = x;
for (int i = 1; i <= 16; i++)
if (dep[x] >= (1 << i)) {
fa[x][i] = fa[fa[x][i - 1]][i - 1];
mx[x][i] = max(mx[x][i - 1], mx[fa[x][i - 1]][i - 1]);
}
else break;
for (int i = fir[x]; ~i; i = e[i].next) {
int v = e[i].to;
dep[v] = dep[x] + 1;
mx[v][0] = val[x];
fa[v][0] = x;
dfs(v);
}
if (x > N) p[++top] = x;
}
void build() {
int ext = N;
sort(&a[1], &a[M + 1]);
for (int i = 1; i <= 2 * N; i++) pa[i] = i;
for (int i = 1; i <= M; i++) {
int x = getf(a[i].u), y = getf(a[i].v);
if (x != y) {
ext++;
pa[x] = pa[y] = ext;
val[ext] = a[i].w;
Add_Edge(ext, y);
Add_Edge(ext, x);
if (ext == 2 * N - 1) break;
}
}
for (int i = 1; i <= N; i++) if (!vis[i]) dfs(getf(i));
for (int i = 1; i <= top; i++) {
int x = p[i];
if (x <= N) insert(rt[i], rt[i - 1], 1, N, h[x]);
else {
rt[i] = rt[i - 1];
if (st[x] == 0) st[x] = i;
else ed[x] = i;
}
}
}
int find(int x, int v) {
for (int i = 17; i >= 0; i--)
if (dep[x] >= (1 << i) && mx[x][i] <= v) x = fa[x][i];
return x;
}
int X[MAX_N];
int main () {
clearGraph();
N = gi(), M = gi(), Q = gi();
for (int i = 1; i <= N; i++) X[i] = h[i] = gi();
for (int i = 1; i <= M; i++) {
int u = gi(), v = gi(), w = gi();
a[i] = (Edge){u, v, w};
}
sort(&X[1], &X[N + 1]);
for (int i = 1; i <= N; i++) h[i] = lower_bound(&X[1], &X[N + 1], h[i]) - X;
build(); int ans = 0;
while (Q--) {
int v = gi(), x = gi(), k = gi();
if (ans != -1) v ^= ans, x ^= ans, k ^= ans;
x = find(v, x);
int l = st[x], r = ed[x];
if (t[rt[r]].v - t[rt[l - 1]].v < k) ans = -1;
else ans = X[query(rt[l], rt[r], 1, N, t[rt[r]].v - t[rt[l - 1]].v - k + 1)];
printf("%d\n", ans);
}
return 0;
}