• [poj2104]可持久化线段树入门题(主席树)


    解题关键:离线求区间第k小,主席树的经典裸题;

    对主席树的理解:主席树维护的是一段序列中某个数字出现的次数,所以需要预先离散化,最好使用vector的erase和unique函数,很方便;如果求整段序列的第k小,我们会想到离散化二分和线段树的做法, 而主席树只是保存了序列的前缀和,排序之后,对序列的前缀分别做线段树,具有差分的性质,因此可以求任意区间的第k小,如果主席树维护索引,只需要求出某个数字在主席树中的位置,即为sort之后v中的索引;若要求第k大,建树时反向排序即可

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<cstdlib>
    #include<iostream>
    #include<cmath>
    using namespace std;
    const int maxn=2e5+10;
    int root[maxn];
    struct node{
        int l,r,sum;
    }p[maxn*20];
    int cnt=0;
    //建树从1开始建
    //rt是当前节点在p数组中的坐标
    int build(int l,int r){
        int rt=++cnt;
        p[rt].sum=0;
        p[rt].l=p[rt].r=0;
        if(l==r) return rt;
        int mid=(l+r)>>1;
        p[rt].l=build(l,mid);
        p[rt].r=build(mid+1,r);
        return rt;
    }//开始先建一棵空树,其实可以动态开点,就是各节点均为0
    int update(int l,int r,int c,int k){//update更新的是索引
        int nc=++cnt;
        p[nc]=p[c];
        p[nc].sum++;
        int mid=(l+r)>>1;
        if(l==r) return nc;
        if(mid>=k) p[nc].l=update(l,mid,p[c].l,k);
        else p[nc].r=update(mid+1,r,p[c].r,k);
        return nc;
    }
    
    int query(int l,int r,int x,int y,int k){
        if(l==r) return l;
        int mid=(l+r)>>1;
        int sum=p[p[y].l].sum-p[p[x].l].sum;
        if(sum>=k) return query(l,mid,p[x].l,p[y].l,k);
        else return query(mid+1,r,p[x].r,p[y].r,k-sum);
    }
    vector<int>v;
    int a[maxn];
    int getid(int x){
        return int(lower_bound(v.begin(),v.end(),x)-v.begin())+1;
    }
    int main(){
        ios::sync_with_stdio(0);
        cin.tie(0);
        cout.tie(0);
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=n;i++){
            cin>>a[i];
            v.push_back(a[i]);
        }
        sort(v.begin(),v.end());
        v.erase(unique(v.begin(), v.end()),v.end());
        //建树过程很重要
        //root[0]=build(1,v.size());//一定注意更新root数组
        //或者上面这句就不需要
        for(int i=1;i<=n;i++){
            root[i]=update(1,n,root[i-1],getid(a[i]));//update是更新某个数出现次数的
        }
        int c,d,q;
        for(int i=0;i<m;i++){
            cin>>c>>d>>q;
            int ans=query(1,n,root[c-1],root[d],q);
            cout<<v[ans-1]<<endl;
        }
        return 0;
    }
  • 相关阅读:
    前置机器学习(一):数学符号及希腊字母
    大神造轮子与小白调包侠#0509
    Windows下的apache maven安装与配置
    Windows下的apache tomcat安装与配置
    C++ <Algorithm>小小总结
    Markdown 小记
    http状态码
    vim命令
    C++ inline
    爬虫前提——正则表达式语法以及在Python中的使用
  • 原文地址:https://www.cnblogs.com/elpsycongroo/p/7296139.html
Copyright © 2020-2023  润新知