題目
n(n<=1e5)座山峯,第i座高度hi(hi<=1e9),保證所有hi不同,
m(m<=5e5)條邊的無向圖,q(q<=5e5)組詢問,
每次詢問給出(v,x,k),
詢問從點v出發,只能走不超過x(x<=1e9)的邊時,
從高到低第k的山峯的點號,不存在輸出-1
思路來源
https://www.cnblogs.com/zwfymqz/p/9683523.html
https://blog.csdn.net/qq_40400202/article/details/102461902?utm_source=app
前置知識(Kruskal重構樹)
圖片來自於自爲風月馬前卒博客,很通俗易懂了,
根x和根y合併的時候,新建一個點T,T同時是x和y的父親,
此時合併並查集,並建樹連邊
性質
- 是一個二叉樹(左右子節點x和y)
- 如果是按最小生成樹建立的話,是一個大根堆(因爲越往上合併權值越大)
- 任意兩個點路徑上邊權的最大值,爲它們的LCA的點權(令路徑上的最大值最小,顯然這條路徑在最小生成樹上)
題解
先建Kruskal重構樹,將父親tot的權值設爲根x和根y的邊權閾值,
倍增預處理父親關係,對於每組詢問,先倍增往上跳到u能跳的最遠祖先anc,
答案就是anc爲根的子樹的葉子結點的從大到小第k,
dfs序維護時間戳+主席樹詢問區間第k大,維護一下即可
代碼
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
const int N=2e5+10,M=5e5+10;
int h[N],x[N],sz;
int n,m,q,u,v,w,k,par[N],tot;
int in[N],out[N],dfn;
int tr[N][2],a[N],fa[N][20];//節點的兩個兒子tr[0]和tr[1],節點的權值a,倍增fa數組
struct zx_tree{
int ls[N*50],rs[N*50],rt[N*50],tot=0,sum[N*50];
inline void upd(int &root,int pre,int l,int r,int pos){
root=++tot;ls[root]=ls[pre],rs[root]=rs[pre],sum[root]=sum[pre]+1;
if(l==r)return ;
int mid=(l+r)/2;
if(pos<=mid)upd(ls[root],ls[pre],l,mid,pos);
else upd(rs[root],rs[pre],mid+1,r,pos);
}
inline int query(int root,int pre,int l,int r,int k){//區間第k小
if(l==r)return l;
int mid=(l+r)/2;int num=sum[ls[root]]-sum[ls[pre]];
if(k<=num)return query(ls[root],ls[pre],l,mid,k);
else return query(rs[root],rs[pre],mid+1,r,k-num);
}
}T;
struct edge{
int u,v,w;
bool operator<(const edge &x){
return w<x.w;
}
}e[M];
int find(int x){
return par[x]==x?x:par[x]=find(par[x]);
}
void merge(int x,int y,int w){
x=find(x),y=find(y);
if(x==y)return;
++tot;//建虛點tot,tot是x和y的父親 權值設爲其間權值w
par[x]=par[y]=fa[x][0]=fa[y][0]=tot;
tr[tot][0]=x;tr[tot][1]=y;
a[tot]=w;
}
void Kruskal(){
sort(e+1,e+m+1);
tot=n;//從n+1開始建虛點tot
for(int i=1;i<=m;++i){
u=find(e[i].u),v=find(e[i].v),w=e[i].w;
merge(u,v,w);
if(tot==2*n-1){
break;
}
}
}
void init(int n){
dfn=sz=0;
fa[0][0]=0;
for(int i=1;i<=2*n;++i){
par[i]=i;
in[i]=out[i]=0;
fa[i][0]=0;
a[i]=0;
tr[i][0]=tr[i][1]=0;
}
}
void discrete(int n){
for(int i=1;i<=n;++i){
scanf("%d",&h[i]);
x[++sz]=h[i];
}
sort(x+1,x+sz+1);
sz=unique(x+1,x+sz+1)-(x+1);
for(int i=1;i<=n;++i){
h[i]=lower_bound(x+1,x+sz+1,h[i])-x;
}
}
void dfs(int u){
for(int i=1;(1<<i)<=n;++i){
fa[u][i]=fa[fa[u][i-1]][i-1];
}
in[u]=++dfn;
if(u<=n){
T.upd(T.rt[dfn],T.rt[dfn-1],1,sz,h[u]);
}
else{
T.rt[dfn]=T.rt[dfn-1];
}
for(int i=0;i<2;++i){
int v=tr[u][i];
if(v){
dfs(v);
}
}
out[u]=dfn;
}
int main(){
scanf("%d%d%d",&n,&m,&q);
//兩兩合併 最多多建n個虛點
init(n);
discrete(n);
for(int i=1;i<=m;++i){
scanf("%d%d%d",&u,&v,&w);
e[i]={u,v,w};
}
Kruskal();
for(int i=1;i<=tot;++i){
if(find(i)!=i)continue;
dfs(i);
}
while(q--){
scanf("%d%d%d",&u,&w,&k);
for(int i=18;i>=0;--i){
if(fa[u][i] && a[fa[u][i]]<=w){
u=fa[u][i];
}
}
int L=T.rt[in[u]-1],R=T.rt[out[u]];
int num=T.sum[R]-T.sum[L],ans=-1;
if(num>=k){
ans=x[T.query(R,L,1,sz,num+1-k)];
}
printf("%d\n",ans);
}
return 0;
}