P4197 Peaks
P7834 [ONTAK2010] Peaks 加强版
加强版:就是多记录一个lastans,同时查询时v、x、k根据式子变换就行了
题意:每次从山v开始,经过困难值不超过x的路径所能到达的山峰中,第k高的山峰的高度。
思路:Kruskal重构树跑dfs序建主席树找第k大(0…0)
因为问题中是困难值不超过x的路径,也就是最大值(极限值)的最小值,所以我们用Kruskal重构树时是最小生成树;
随后就是再dfs序中找第k大值了,没有k个数字输出-1。上主席树就行了。
//https://www.luogu.com.cn/problem/P4197 kruskal重构树+主席树
#include<bits/stdc++.h>
using namespace std;
const int N=8e5+5;
int n,m,q,h[N];
int to[N],ne[N],val[N],fir[N],idx,cnt;
int fa[N],f[N][25];//树上倍增
int L[N],R[N],nu;//dfs序
int root[N],id,sz;//sz为离散化后num大小
vector<int>num;
struct node{int ls,rs,sum;}tr[N*30];//主席树
struct Edge{int u,v,w;}edge[N];
bool cmp(Edge l,Edge r){return l.w<r.w;}//最小生成树
void add(int u,int v){to[idx]=v;ne[idx]=fir[u];fir[u]=idx++;}//链式前
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}//并查集
void disc(){//离散化
sort(num.begin(),num.end());
num.erase(unique(num.begin(),num.end()),num.end());
sz=num.size();
for(int i=1;i<=n;i++) h[i]=lower_bound(num.begin(),num.end(),h[i])-num.begin()+1;
}
void input(){
memset(fir,-1,sizeof(fir));
scanf("%d%d%d",&n,&m,&q); cnt=n;//已经有n个点,下个节点编号从n+1起
for(int i=1;i<=n;i++) scanf("%d",&h[i]),num.push_back(h[i]);
for(int i=1;i<=m;i++) scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
}
void Kruskal(){
sort(edge+1,edge+m+1,cmp);
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++){
int fu=find(edge[i].u),fv=find(edge[i].v),w=edge[i].w;
if(fu==fv) continue;
val[++cnt]=w;
fa[cnt]=fa[fu]=fa[fv]=cnt;
add(cnt,fu),add(cnt,fv);//节点cnt是fu、fv的父亲节点
}
}
int build(int l,int r){
int p=++id;
if(l==r) return p;
int mid=(l+r)>>1;
tr[p].ls=build(l,mid),build(mid+1,r);
return p;
}
int insert(int p,int l,int r,int x){//主席树板子,插数
int q=++id;
tr[q]=tr[p];
if(l==r){
tr[q].sum++;
return q;
}
int mid=(l+r)>>1;
if(x<=mid) tr[q].ls=insert(tr[p].ls,l,mid,x);
else tr[q].rs=insert(tr[p].rs,mid+1,r,x);
tr[q].sum=tr[tr[q].ls].sum+tr[tr[q].rs].sum;
return q;
}
void dfs(int u,int father){
f[u][0]=father;
for(int i=1;i<=22;i++) f[u][i]=f[f[u][i-1]][i-1];
L[u]=nu;
if(fir[u]==-1){//叶子节点,就加入
L[u]=++nu;
root[nu]=insert(root[nu-1],1,sz,h[u]);
}
for(int i=fir[u];~i;i=ne[i]) dfs(to[i],u);
R[u]=nu;
}
int query(int q,int p,int l,int r,int kth){//主席树板子,区间第k大
if(l==r) return l;
int sum=tr[tr[p].rs].sum-tr[tr[q].rs].sum;
int mid=(l+r)>>1;
if(kth<=sum) return query(tr[q].rs,tr[p].rs,mid+1,r,kth);
else return query(tr[q].ls,tr[p].ls,l,mid,kth-sum);
}
void Ac(){
root[0]=build(1,sz);
dfs(cnt,0);
while(q--){
int v,x,k; scanf("%d%d%d",&v,&x,&k);
for(int i=22;i>=0;i--){
if(f[v][i]&&val[f[v][i]]<=x){//从x出发,也就是一颗包含x的子树,倍增找到子树的根
v=f[v][i];
}
}
if(tr[root[R[v]]].sum-tr[root[L[v]]].sum<k) puts("-1");
else printf("%d\n",num[query(root[L[v]],root[R[v]],1,sz,k)-1]);
}
}
int main(){
input();
disc();
Kruskal();
Ac();
}