• 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;
    }
    
    
  • 相关阅读:
    2018.10.22-ssoi3979荔枝丹(litchi)
    2018.10.18- oj3969 pd
    2018.10.18-ssoj3970 graph
    【2019年8月版】OCP 071认证考试原题-第38题
    【2019年8月版】OCP 071认证考试原题-第37题
    【2019年8月版】OCP 071认证考试原题-第36题
    【2019年8月版】OCP 071认证考试原题-第35题
    【2019年8月版】OCP 071认证考试原题-第34题
    【2019年8月版】OCP 071认证考试原题-第33题
    【2019年8月】OCP 071认证考试最新版本的考试原题-第32题
  • 原文地址:https://www.cnblogs.com/santiego/p/11752328.html
Copyright © 2020-2023  润新知