Peaks 线段树合并
Peaks 线段树合并
\(n\)个带权值\(h_i\)山峰,有\(m\)条山峰间双向道路,\(q\)组询问,问从\(v_i\)开始只经过\(h_i\le x\)的路径所能到达的山峰中第\(k\)高的山峰,如果无解输出\(-1\)
线段树合并好题。吊打主席树、Kruskal重构树的典范
首先发现可以离线,我们将所有询问按\(x\)排序,随着询问再去加边,这样可以去掉路径上\(h_i\le x\)这一条件使问题极大简化。
然后从\(v_i\)开始能经过的所有山峰可以看做联通块,于是我们愉快地用并查集维护,用权值线段树查询第\(k\)大,合并联通块时合并权值线段树即可。很像[HNOI2012]永无乡。
另外注意此题是查询第\(k\)大,不是第\(k\)小。
#include <cstdio>
#include <algorithm>
#define MAXN 100010
#define MAXM 500005
using namespace std;
int n,m,q;
inline int read(){
char ch=getchar();int s=0;
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') s=s*10+ch-'0', ch=getchar();
return s;
}
int h[MAXN],h_sort[MAXN],rnk[MAXN];
struct nod{
int u,v,val;
} edge[MAXM];
bool cmp_nod(const nod &a, const nod &b){
return a.val<b.val;
}
struct question{
int v,x,k;
int id;
} qs[MAXM];
bool cmp_qs(const question &a, const question &b){
return a.x<b.x;
}
int res[MAXM];
int fa[MAXN];
int get_fa(int x){
if(fa[x]==x) return x;
return fa[x]=get_fa(fa[x]);
}
#define MAXT MAXN*20
int rot[MAXN],tot,s;
int tre[MAXT],sl[MAXT],sr[MAXT];
void change(int &x, int l, int r, int pos){
if(x==0) x=++tot;
tre[x]+=1;
if(l==r) return;
int mid=(l+r)>>1;
if(pos<=mid) change(sl[x], l, mid, pos);
else change(sr[x], mid+1, r, pos);
}
int query(int x, int l, int r, int k){
if(x==0||tre[x]<k) return 0; // 如果不存在则返回0
if(l==r) return l;
int mid=(l+r)>>1;
if(tre[sr[x]]>=k) return query(sr[x], mid+1, r, k); // 找第k大所以先找右儿子
else return query(sl[x], l, mid, k-tre[sr[x]]);
}
int merge(int a, int b, int l, int r){
if(a==0||b==0) return a+b;
if(l==r){
tre[a]+=tre[b];
return a;
}
int mid=(l+r)>>1;
sl[a]=merge(sl[a], sl[b], l, mid);
sr[a]=merge(sr[a], sr[b], mid+1, r);
tre[a]=tre[sl[a]]+tre[sr[a]]; // 记得合并后更新
return a;
}
void merge_edge(int x){
int fa1=get_fa(edge[x].u),fa2=get_fa(edge[x].v);
if(fa1==fa2) return; // 如果已经都联通了则不需要合并线段树了
fa[fa2]=fa1;
rot[fa1]=merge(rot[fa1], rot[fa2], 1, s);
}
int main(){
n=read(),m=read(),q=read();
for(int i=1;i<=n;++i) fa[i]=i;
for(int i=1;i<=n;++i) h_sort[i]=h[i]=read();
sort(h_sort+1, h_sort+1+n);
s=unique(h_sort+1, h_sort+1+n)-(h_sort+1);
for(int i=1;i<=n;++i){
rnk[i]=lower_bound(h_sort+1, h_sort+1+s, h[i])-h_sort; // 离散化
change(rot[i], 1, s, rnk[i]);
}
for(int i=1;i<=m;++i) edge[i].u=read(),edge[i].v=read(),edge[i].val=read();
sort(edge+1, edge+1+m, cmp_nod);
for(int i=1;i<=q;++i) qs[i].v=read(),qs[i].x=read(),qs[i].k=read(),qs[i].id=i;
sort(qs+1, qs+1+q, cmp_qs);
int pos=1;
for(int i=1;i<=q;++i){
int x=qs[i].x,v=qs[i].v,k=qs[i].k;
while(pos<=m&&edge[pos].val<=x) merge_edge(pos),++pos; // 把小于等于x的边加入
int ans=query(rot[get_fa(v)], 1, s, k);
if(ans==0) res[qs[i].id]=-1;
else res[qs[i].id]=h_sort[ans]; // 最后答案不是下标而是对应的值
}
for(int i=1;i<=q;++i) printf("%d\n", res[i]);
return 0;
}