主席树(静态区间第k大)+poj2014+poj2761+hdu2665

终于开始看主席树了

主席树就是对数组[1...n](建设排序去重后一共有m个不同的数字)的每个前缀[1...i]建立一颗线段树,记录从1到i出现在(1-m)的数有多少个,比如对于i=x,cnt[x]表示出现在(1-m)一共有多少个,他的左孩子lson表示前缀1-x出现在(1-m/2)中出现的次数。

关于主席树详细的讲解请看主席树读书笔记


下面附上poj2104主席树代码,原来用划分树写过

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=100010;
const int maxm=maxn*30;
int n,q,m;
int a[maxn];
struct TREE
{
    int order[maxn];
    int T[maxm],cnt[maxm],lson[maxm],rson[maxm];
    int num;
    void init()
    {
        for(int i=1;i<=n;i++)order[i]=a[i];
        sort(order+1,order+1+n);
        m=unique(order+1,order+1+n)-order-1;
        num=0;
    }
    int build(int l,int r)
    {
        int root=num++;
        cnt[root]=0;
        if(l==r)return root;
        int mid=(l+r)>>1;
        lson[root]=build(l,mid);
        rson[root]=build(mid+1,r);
        return root;
    }
    int update(int root,int pos,int val)
    {
        int newroot=num++,tmp=newroot;
        cnt[newroot]=cnt[root]+val;
        int l=1,r=m;
        while(l<r)
        {
            int mid=(l+r)>>1;
            if(pos<=mid)
            {
                r=mid;
                lson[newroot]=num++;rson[newroot]=rson[root];
                newroot=lson[newroot],root=lson[root];
            }
            else
            {
                l=mid+1;
                lson[newroot]=lson[root],rson[newroot]=num++;
                newroot=rson[newroot],root=rson[root];
            }
            cnt[newroot]=cnt[root]+val;
        }
        return tmp;
    }
    int query(int left,int right,int k)
    {
        int l=1,r=m;
        while(l<r)
        {
            int mid=(l+r)>>1;
            if(cnt[lson[left]]-cnt[lson[right]]>=k)
            {
                r=mid;
                left=lson[left];
                right=lson[right];
            }
            else
            {
                l=mid+1;
                k-=(cnt[lson[left]]-cnt[lson[right]]);
                left=rson[left];
                right=rson[right];
            }
        }
        return l;
    }
}tree;
int main()
{
    while(scanf("%d%d",&n,&q)!=EOF)
    {
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        tree.init();
        tree.T[n+1]=tree.build(1,m);
        for(int i=n;i>0;i--)
        {
            int pos=lower_bound(tree.order+1,tree.order+1+m,a[i])-tree.order;
            tree.T[i]=tree.update(tree.T[i+1],pos,1);
        }
        while(q--)
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            printf("%d\n",tree.order[tree.query(tree.T[l],tree.T[r+1],k)]);
        }
    }
    return 0;
}

2761跟上面是一样的

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=100010;
const int maxm=maxn*20;
int a[maxn],order[maxn],cnt[maxm],lson[maxm],rson[maxm],T[maxm];
int n,m,q,num;
void init()
{
    num=0;
    for(int i=1;i<=n;i++)order[i]=a[i];
    sort(order+1,order+1+n);
    m=unique(order+1,order+1+n)-order-1;
}
int find(int x)
{
    return lower_bound(order+1,order+1+m,x)-order;
}
int  build(int l,int r)
{
    int root=num++;
    cnt[root]=0;
    if(l!=r)
    {
        int mid=(l+r)>>1;
        lson[root]=build(l,mid);
        rson[root]=build(mid+1,r);
    }
    return root;
}
int update(int root,int pos,int val)
{
    int newroot=num++,tmp=num-1;
    int l=1,r=m;
    cnt[newroot]=cnt[root]+val;
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(pos<=mid)
        {
            r=mid;
            lson[newroot]=num++,rson[newroot]=rson[root];
            newroot=lson[newroot];root=lson[root];
        }
        else
        {
            l=mid+1;
            rson[newroot]=num++,lson[newroot]=lson[root];
            newroot=rson[newroot],root=rson[root];
        }
        cnt[newroot]=cnt[root]+val;
    }
    return tmp;
}
int query(int left,int right,int k)
{
    int l=1,r=m;
    while(l<r)
    {
        int mid=(l+r)>>1;
        int tmp=cnt[lson[right]]-cnt[lson[left]];
        if(tmp>=k)
        {
            r=mid;
            right=lson[right];
            left=lson[left];
        }
        else
        {
            k-=tmp;
            l=mid+1;
            right=rson[right];
            left=rson[left];
        }
    }
    return l;
}
int main()
{
    while(scanf("%d%d",&n,&q)!=EOF)
    {
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        init();
        T[0]=build(1,m);
        for(int i=1;i<=n;i++)
            T[i]=update(T[i-1],find(a[i]),1);
        while(q--)
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            printf("%d\n",order[query(T[l-1],T[r],k)]);
        }
    }
    return 0;
}


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值