Peaks 线段树合并

Peaks 线段树合并

(n)个带权值(h_i)山峰,有(m)条山峰间双向道路,(q)组询问,问从(v_i)开始只经过(h_ile x)的路径所能到达的山峰中第(k)高的山峰,如果无解输出(-1)

线段树合并好题。吊打主席树、Kruskal重构树的典范

首先发现可以离线,我们将所有询问按(x)排序,随着询问再去加边,这样可以去掉路径上(h_ile 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
", res[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/santiego/p/11752328.html