zoukankan      html  css  js  c++  java
  • 主席树

    主席树是 以前缀和形式基于权值线段树建立的可持久化线段树,可持久化指的是它保存了这棵树的所有历史版本.

    最简单的办法是:如果你输入了n个数,那么每输入一个数字a[i],就构造一棵保存了从a[1]到a[i]的权值线段树,由于只增加了logn的节点数,我们增加改变的节点并将没有改变的子树指向该节点,这样需要的空间开销只有n*(4+logn)

    我们可以把第j棵树和第(i-1)棵树上的每个点的权值相减,来得到一棵新的权值线段树,而这个新的权值线段树相当于是输入了a[i]到a[j]以后得到的。

    模板题 K-th Number

    后面将第 k小/大 说成kth 

    解决什么问题:
    给定一段区间,静态求区间kth 

    想想方法:

    暴力:对于每一个询问,排个序,就行了,时间复杂度O(nmlogn) 

    莫队+树状数组:树状数组可以求给定区间kth kthkth,使用二分+树状数组,具体不展开,但是多个区间的话,需要不断地进行树状数组的add/del操作,那么使用莫队来优化区间端点的移动问题,时间复杂度O((n+m)√n logn) 莫队复杂度*树状数组复杂度

    莫队+平衡树:把树状数组的部分替换成二叉查找树,用splay的一部分操作,需要用到kth操作,不用翻转标记什么的,时间复杂度O((n+m)√n logn)跟上面的一样

    目前想想,也就这三种方法,各有优劣,暴力时间复杂度不行,但是可以在线
    后面两种因为莫队的原因必须离线

    但是这三种方法时间都太慢,这个题目我们需要一个O(nlogn) 的做法

    于是主席树就诞生了

    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll ;
    const int oo=0x7f7f7f7f ;
    const int maxn=1e5+7;
    const int mod=1e9+7;
    int n,m,cnt,root[maxn],a[maxn],x,y,k;
    struct node{
        int l,r,sum;
    }T[maxn*25];
    vector<int> v;
    int getid(int x){
        return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
    }
    void update(int l,int r,int &x,int y,int pos){
        T[++cnt]=T[y],T[cnt].sum++,x=cnt;
        if(l==r) return;
        int mid=(l+r)/2;
        if(mid>=pos) update(l,mid,T[x].l,T[y].l,pos);
        else update(mid+1,r,T[x].r,T[y].r,pos);
    }
    int query(int l,int r,int x,int y,int k){
        if(l==r) return l;
        int mid=(l+r)/2;
        int sum=T[T[y].l].sum-T[T[x].l].sum;
        if(sum>=k) return query(l,mid,T[x].l,T[y].l,k);
        else return query(mid+1,r,T[x].r,T[y].r,k-sum);
    }
    int main(void){
        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[i],root[i-1],getid(a[i]));
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&x,&y,&k);
            printf("%d
    ",v[query(1,n,root[x-1],root[y],k)-1]);
        }
     
        return 0;
    }
    View Code

    参考博客:

    https://blog.csdn.net/Stupid_Turtle/article/details/80445998

    https://blog.csdn.net/ModestCoder_/article/details/90107874

  • 相关阅读:
    Build a pile of Cubes
    一键升级所有pip过期库
    AWGN
    调制详解——待完善
    BASK、BFSK、BPSK调制方法的Matlab程序实现
    tomcat运行问题解决方法
    ehcache简单使用
    MySQL 数据库中用户表中口令登陆设置
    和自己赛跑的人
    中文词频统计
  • 原文地址:https://www.cnblogs.com/young-children/p/11787490.html
Copyright © 2011-2022 走看看