zoukankan      html  css  js  c++  java
  • 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;
    }
    
    
  • 相关阅读:
    使用ASP的优势和劣势
    rman的catalog命令
    sql exist和in的区别及查询效率比较
    zblog模板修改字体大小的教程
    js 刷新和关闭页面触发的事件 及操作COOKIE
    Flex走出低谷的关键: 打破Flex固有的定义
    服务网健康日志中 radio传值解决方案
    sql语句查询出表里的第二条、第三条记录(附加多个条件)
    鼠标经过超链接文字变色
    (asp.net)鼠标放上去的时候文本框的提示消失,鼠标离开又重新显示
  • 原文地址:https://www.cnblogs.com/santiego/p/11752328.html
Copyright © 2011-2022 走看看