P4197 Peaks
题面:
题目描述
在 Bytemountains 有 n n n 座山峰,每座山峰有他的高度 h i h_i hi。有些山峰之间有双向道路相连,共 m m m 条路径,每条路径有一个困难值,这个值越大表示越难>走。
现在有 q q q 组询问,每组询问询问从点 v v v 开始只经过困难值小于等于 x x x 的路径所能到达的山峰中第 k k k 高的山峰,如果无解输出 − 1 -1 −1。
输入格式
第一行三个数 n , m , q n,m,q n,m,q。
第二行 n n n 个数,第 i i i 个数为 h i h_i hi。接下来 m m m 行,每行三个整数 a , b , c a,b,c a,b,c,表示从 a → b a \to b a→b 有一条困难值为 c c c 的双向路径。
接下来 q q q 行,每行三个数 v , x , k v,x,k v,x,k,表示一组询问。输出格式
对于每组询问,输出一个整数表示能到达的山峰中第 k k k 高的山峰的高度。
样例 #1
样例输入 #1
10 11 4 1 2 3 4 5 6 7 8 9 10 1 4 4 2 5 3 9 8 2 7 8 10 7 1 4 6 7 1 6 4 8 2 1 5 10 8 10 3 4 7 3 4 6 1 5 2 1 5 6 1 5 8 8 9 2
样例输出 #1
6 1 -1 8
提示
数据规模与约定
对于 100 % 100\% 100% 的数据, n ≤ 1 0 5 n \le 10^5 n≤105, 0 ≤ m , q ≤ 5 × 1 0 5 0 \le m,q \le 5\times 10^5 0≤m,q≤5×105, h i , c , x ≤ 1 0 9 h_i,c,x \le 10^9 hi,c,x≤109。
不难看出本题是 Kruskal 重构树
建出 Kruskal 最小树重构树后倍增找到可达的最远的 lca ,
然后,对于子树中的叶子进行第 k k k 大查询!
然而叶子的编号不是连续的,我们很难用什么数据结构维护。
考虑改变编号,我们用 dfs 序去遍历树,如图:
我们发现,因为 dfs 序的性质,一个节点的子树被访问完了之后,才会退出该节点,所以一个子树的叶子节点一定构成一个连续区间,
这样我们就将题目变成了区间第 k k k 大查询,用可持久化线段树就可以维护出来了.
因为可持久化线段树维护的值可以离散化,果断离散化减少空间
#include<bits/stdc++.h>
using namespace std;
#define int long long
int rd() {
int x = 0, w = 1;
char ch = 0;
while (ch < '0' || ch > '9') {
if (ch == '-') w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = getchar();
}
return x * w;
}
void wt(int x) {
static int sta[35];
int f = 1;
if(x < 0) f = -1,x *= f;
int top = 0;
do {
sta[top++] = x % 10, x /= 10;
} while (x);
if(f == -1) putchar('-');
while (top) putchar(sta[--top] + 48);
}
const int N = 1e5+5,M = N * 16,maxn = N * 16;
int n,m,q,h[maxn],s[maxn],k,v[maxn];
array<int,3> e[M];
int head[maxn],nxt[maxn<<1],to[maxn<<1],cnt;
void init() {memset(head,-1,sizeof(head));}
int find(int x) {
if(s[x] ^ x) s[x] = find(s[x]);
return s[x];
}
void add(int u,int V) {
nxt[cnt] = head[u];
to[cnt] = V;
head[u] = cnt++;
}
void kruskal() {
k = n;
int tot = 0;
for(int i = 0;i<maxn;i++) s[i] = i;
sort(e + 1,e + m + 1);
for(int i = 1;i<=m;i++) {
int fx = find(e[i][1]),fy = find(e[i][2]);
if(fx ^ fy) {
s[fx] = s[fy] = ++k;
add(fx,k);add(fy,k);
add(k,fx);add(k,fy);
v[k] = e[i][0];
tot++;
if(tot == n - 1) break;
}
}
}
int fa[maxn][21],hd[maxn][21],L[maxn],R[maxn],tot;
int rt[maxn],lst[maxn];
namespace sgt{
#define mid ((pl + pr) >> 1)
int rot,siz[N*50],ls[N*50],rs[N*50];
int update(int f,int pl,int pr,int x) {
int rt = ++rot;
ls[rt] = ls[f];
rs[rt] = rs[f];
siz[rt] = siz[f] + 1;
if(pl < pr) {
if(x <= mid) ls[rt] = update(ls[f],pl,mid,x);
else rs[rt] = update(rs[f],mid+1,pr,x);
}
return rt;
}
int query(int x,int y,int pl,int pr,int K) {
if(siz[y] - siz[x] < K) return -1;
if(pl == pr) return pl;
int sz = siz[rs[y]] - siz[rs[x]];
if(K <= sz) return query(rs[x],rs[y],mid + 1,pr,K);
else return query(ls[x],ls[y],pl,mid,K - sz);
}
}
void dfs(int x,int f) {
fa[x][0] = f;hd[x][0] = v[fa[x][0]];
for(int i = 1;i<=20;i++) {
fa[x][i] = fa[fa[x][i - 1]][i - 1];
hd[x][i] = min(hd[fa[x][i - 1]][i - 1],hd[x][i]);
}
for(int i = head[x];~i;i = nxt[i]) {
int y = to[i];
if(y ^ f) {
dfs(y,x);
L[x] = min(L[x],L[y]);
R[x] = max(R[x],R[y]);
}
}
if(x <= n) {
R[x] = L[x] = ++tot;
rt[tot] = sgt::update(rt[tot - 1],1,N,h[x]);
}
}
signed main() {
init();
n = rd(),m = rd(),q = rd();
for(int i = 1;i<=n;i++) lst[i] = h[i] = rd();
for(int i = 1;i<=m;i++)
e[i][1] = rd(),e[i][2] = rd(),e[i][0] = rd();
sort(lst + 1,lst + n + 1);
int top = unique(lst + 1,lst + n + 1) - lst - 1;
for(int i = 1;i<=n;i++)
h[i] = lower_bound(lst + 1,lst + top + 1,h[i]) - lst;
kruskal();
memset(L,0x3f,sizeof(L));
memset(hd,0x3f,sizeof(hd));
v[0] = 0x3f3f3f3f3f3f3f3fLL;
dfs(k,0);
while(q--) {
int S = rd(),x = rd(),K = rd();
for(int i = 20;i >= 0;i--)
if(hd[S][i] <= x)
S = fa[S][i];
if(sgt::siz[rt[R[S]]] - sgt::siz[rt[L[S] - 1]] < K) {puts("-1");continue;}
int ans = sgt::query(rt[L[S] - 1],rt[R[S]],1,N,K);
if(ans == -1) puts("-1");
else wt(lst[ans]),putchar('\n');
}
return 0;
}