主席树:LuoguP3834 【模板】可持久化线段树 1(主席树)

版权声明:原创文章,转载要注明作者哦 https://blog.csdn.net/DYT_B/article/details/81432768

题目描述戳这里
题解
话说我去年好像学过主席树。。。
然后我就不会了,然后我就害怕的又学了一次。
主席树其实就是线段树的优化。
我们考虑这道模板题。
如果用暴力的方法做,肯定会Tle。
那么我们想一想能不能用线段树来优化一下。
先简单化一下题目,如果求的是1~m(m<=n)的第k大值,怎么用线段树做?
那么我们就可以建立一颗权值(权值是点权离散化之后的位置)线段树,每一个点的权值就是它所包含的权值区间内的点的总个数。那么如果要搜索第k大值,在某一个点我们只要判断一下它的左子树中点的个数c是否大于k,如果是,就走左子树;否则走右子树,k变成k-c。

那么如果求的是从x~m(x<=m<=n)呢?
和上面的做法类似,只要建立两颗权值线段树(1~x和1~m),在某一个节点将两个sum相减作为c就行了。

有了以上的方法,我们就可以做这道题啦。我们只要对于每一个前缀,造一颗权值线段树就行啦。
但是,我们发现这样的内存很大,大概是 n n l o g ( n ) ,必然会爆炸。
但是我们发现对于两个相邻的前缀i和i-1,它们的大多数部分都是一样的,实际上不一样的部分只有1~pos[i]这一段长度为log(n)的链是不一样的,那么我们就可以通过合并操作来共享一些节点。这样复杂度就比较科学啦。。。

代码如下(比较简短):

#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=200005;
int tot,n,m,a[maxn],b[maxn],root[maxn];
struct dyt{
    int sum,lson,rson;
}tree[maxn*20];
int getpos(int x){
    int l=1,r=n;
    while (l<=r) {
        int mid=(l+r)>>1;
        if (x==b[mid]) return mid;
        if (x<b[mid]) r=mid-1; else l=mid+1;
    }
    return -1;
}
void insert(int x,int l,int r,int pos){
    tree[x].sum++; if (l>=r) return;
    int mid=(l+r)>>1;
    if (pos<=mid) {
        tree[x].lson=++tot; insert(tree[x].lson,l,mid,pos);
    } else {
        tree[x].rson=++tot; insert(tree[x].rson,mid+1,r,pos);
    }
}
void merge(int x,int y){
    tree[x].sum+=tree[y].sum;
    if (!tree[x].lson) tree[x].lson=tree[y].lson; else merge(tree[x].lson,tree[y].lson);
    if (!tree[x].rson) tree[x].rson=tree[y].rson; else merge(tree[x].rson,tree[y].rson);
}
int query(int x,int y,int l,int r,int k){
    if (l==r) return b[l];
    int mid=(l+r)>>1;
    if (tree[tree[y].lson].sum-tree[tree[x].lson].sum>=k) return query(tree[x].lson,tree[y].lson,l,mid,k);
    else return query(tree[x].rson,tree[y].rson,mid+1,r,k-(tree[tree[y].lson].sum-tree[tree[x].lson].sum));
}
int main(){
    scanf("%d %d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
    sort(b+1,b+1+n); tot=1;
    for (int i=1;i<=n;i++) {
        root[i]=++tot;
        insert(root[i],1,n,getpos(a[i]));
        merge(root[i],root[i-1]);
    }
    for (int i=1;i<=m;i++) {
        int x,y,z; scanf("%d %d %d",&x,&y,&z);
        printf("%d\n",query(root[x-1],root[y],1,n,z));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/DYT_B/article/details/81432768