zoukankan      html  css  js  c++  java
  • 主席树(模板)

    求区间第K大的值;

    我们需要在短时间内回答数目巨大的问题,这个算法的核心是空间换时间;

    每个点建一个线段树,是的;

    我们先离散化所有权值,使得当前的权值在1到n范围内,恰巧是vector里的下标;

    对于每一个点,我们分成左二子和右儿子,分别存放当前区间的左半部分和右半部分,维护左右节点的数量;

    我们怎么求区间第K大呢?

    运用前缀和的思想,我们其实建出来的很多树左右数量是递增的;

    遍历每一个节点,现将当前节点继承前一节点的历史状态,再将节点数+1,根据当前节点的大小判断插在左边还是右边,递归进行此操作;

    对于区间【l,r】,我们将l-1的树拿出来,r的树拿出来,每个树的节点数就是1到 i 的节点数,先两个左子树的节点数相减,得到的数与当前k比较,

    如果大于k就在左子树里找,如果小于k就在右子树里找k-sum_L个;也是递归实现;

    实质就是每次加点,查询时套用前缀和,根据数量判断向下递归的方向,从而减少时间复杂度;

    时刻注意每个点代表的历史状态,谁和谁对应;

    离散化操作

        sort(v.begin(),v.end());
        v.erase(unique(v.begin(),v.end()),v.end());

    先将数组排序,然后unique(),此时函数操作是将重复的数字都扔到后面,返回不重复序列的最后一个数的下一个数的下标;这时我们将后面删去即可;

    取得离散后的数lower_bound即可;

    根据学长的传承,空间开40倍;

    section (区间)

    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=2e5+10;
    vector<int> v;
    int root_sec[maxn];
    struct node
    {
        int l,r,sum_pos;
    }t[maxn*40];
    
    int get_id(int x)
    {
        return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
    }
    
    int n,m,a[maxn],cnt;
    
    void update(int l,int r,int x,int &y,int pos)
    {
        y=++cnt;t[y]=t[x];t[y].sum_pos++;
        if(l==r) return ;
        int mid=(l+r)>>1;
        if(pos<=mid) update(l,mid,t[x].l,t[y].l,pos);
        else update(mid+1,r,t[x].r,t[y].r,pos);
    }
    
    int query_id(int l,int r,int x,int y,int sum_k)
    {
        if(l==r) return l;
        int sum_sec=t[t[y].l].sum_pos-t[t[x].l].sum_pos;
        int mid=(l+r)>>1;
        if(sum_sec>=sum_k) return query_id(l,mid,t[x].l,t[y].l,sum_k);
        else return query_id(mid+1,r,t[x].r,t[y].r,sum_k-sum_sec);
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            v.push_back(a[i]);
        }
        sort(v.begin(),v.end());
        v.erase(unique(v.begin(),v.end()),v.end());
        for(int i=1;i<=n;i++) update(1,n,root_sec[i-1],root_sec[i],get_id(a[i]));
        for(int i=1;i<=m;i++)
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            printf("%d
    ",v[query_id(1,n,root_sec[l-1],root_sec[r],k)-1]);
        }
        return 0;
    }

    至于为什么叫主席树,是因为发明他的人叫HJT;

  • 相关阅读:
    [Everyday Mathematics]20150226
    [Everyday Mathematics]20150225
    [Everyday Mathematics]20150224
    [Everyday Mathematics]20150223
    [Everyday Mathematics]20150222
    [Everyday Mathematics]20150221
    [Everyday Mathematics]20150220
    [Everyday Mathematics]20150219
    [Everyday Mathematics]20150218
    [Everyday Mathematic]20150217
  • 原文地址:https://www.cnblogs.com/WHFF521/p/11623322.html
Copyright © 2011-2022 走看看