区间第k大

 归并树:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<queue>
#include<map>
#include<cmath>
#include<set>
#include<stack>
#define ll long long
#define max(x,y) (x)>(y)?(x):(y)
#define min(x,y) (x)>(y)?(y):(x)
#define cls(name,x) memset(name,x,sizeof(name))
using namespace std;
const int inf=1<<28;
const int maxn=50010;
const int maxm=110;
const int mod=1e9+7;
const double pi=acos(-1.0);
int n;
struct node
{
    int *num;
    int len;
}tree[maxn*4];
int num[maxn];
void build(int l,int r,int x)
{
    tree[x].len=r-l+1;
    tree[x].num=new int[tree[x].len];
    if(l==r)
    {
        tree[x].num[0]=num[l];
        return ;
    }
    int mid=(l+r)/2;
    build(l,mid,x*2);
    build(mid+1,r,x*2+1);
    int i=0,j=0,k=0;
    while(i<tree[x*2].len&&j<tree[x*2+1].len)
    {
        if(tree[x*2].num[i]<tree[x*2+1].num[j])
            tree[x].num[k++]=tree[x*2].num[i++];
        else
            tree[x].num[k++]=tree[x*2+1].num[j++];
    }
    while(i<tree[x*2].len)
        tree[x].num[k++]=tree[x*2].num[i++];
    while(j<tree[x*2+1].len)
        tree[x].num[k++]=tree[x*2+1].num[j++];
}
int querry(int ql,int qr,int temp,int l,int r,int x)
{
    if(ql==l&&qr==r)
    {
        int tl=0,tr=tree[x].len;
        while(tl<tr)
        {
            int mm=(tl+tr)/2;
            if(tree[x].num[mm]<temp)
                tl=mm+1;
            else
                tr=mm;
        }
        return tl;
    }
    int mid=(l+r)/2;
    if(qr<=mid)
        return querry(ql,qr,temp,l,mid,x*2);
    else if(ql>=mid+1)
        return querry(ql,qr,temp,mid+1,r,x*2+1);
    else
        return querry(ql,mid,temp,l,mid,x*2)+querry(mid+1,qr,temp,mid+1,r,x*2+1);
}
int main()
{
    //freopen("in.txt","r",stdin);
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++)
            scanf("%d",&num[i]);
        build(1,n,1);
        int q;
        scanf("%d",&q);
        while(q--)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);//k有b-a-c个小于它的数
            int l=1,r=n;
            while(l<r)
            {
                int mid=(l+r)/2;
                int qc=querry(a+1,b+1,tree[1].num[mid],1,n,1);
                if(qc<=b-a-c+1)
                    l=mid;
                else
                    r=mid;
                if(l+1==r) break;
            }
            if(querry(a+1,b+1,tree[1].num[r],1,n,1)==b-a-c+1)
                printf("%d\n",tree[1].num[r]);
            else
                printf("%d\n",tree[1].num[l]);
        }
    }
    return 0;
}

划分树:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<queue>
#include<map>
#include<cmath>
#include<set>
#include<stack>
#define ll long long
#define pb push_back
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
#define cls(name,x) memset(name,x,sizeof(name))
#define fs first
#define sc second
#define mp make_pair
#define L(x) (1<<x)
#define next Next
using namespace std;
const int inf=1e9+10;
const ll llinf=1e16+10;
const int maxn=4e5+10;
const int maxm=1e3+10;
const int mod=1e9+7;
int n,m;
struct node
{
    int *num;
    int *c;//[l,i]有多少个数进入左子树
}tree[maxn*4];
int sorted[maxn];
void init(int l,int r,int x)
{
    tree[x].num=new int[r-l+2];
    tree[x].c=new int[r-l+2];
    tree[x].c[0]=0;
    if(l==r) return ;
    int mid=(l+r)/2;
    init(l,mid,x*2);
    init(mid+1,r,x*2+1);
}
void build(int l,int r,int x)//必须是稳定的快排
{
    if(l==r) return ;
    int mid=(l+r)/2;
    int key=sorted[mid];
    int k1=0,k2=0;//进入左右子树的数量
    int c=0;//小于key必进入左子树数量
    for(int i=1;i<=r-l+1;i++)
        if(tree[x].num[i]<key) c++;
    c=mid-l+1-c;//等于key可进入左子树数量
    for(int i=1;i<=r-l+1;i++)
    {
        if(tree[x].num[i]<key)
            tree[x*2].num[1+k1++]=tree[x].num[i];
        else if(tree[x].num[i]==key)
        {
            if(c)
            {
                tree[x*2].num[1+k1++]=tree[x].num[i];
                c--;
            }
            else tree[x*2+1].num[1+k2++]=tree[x].num[i];
        }
        else tree[x*2+1].num[1+k2++]=tree[x].num[i];
        tree[x].c[i]=k1;
    }
    build(l,mid,x*2);
    build(mid+1,r,x*2+1);
}
int query(int ql,int qr,int k,int l,int r,int x)//返回[ql,qr]第k小的数
{
    if(l==r)
        return tree[x].num[1];
    int cl=tree[x].c[qr-l+1]-tree[x].c[ql-1-l+1];//[ql,qr]中有多少进入了左子树
    int cr=qr-ql+1-cl;//[ql,qr]中有多少进入了右子树
    int mid=(l+r)/2;
    int tl=tree[x].c[ql-1-l+1]-tree[x].c[l-1-l+1];//[l,ql-1]中有多少进入了左子树
    int tr=ql-1-l+1-tl;//[l,ql-1]中有多少进入了右子树
    if(k<=cl)
        return query(l+tl,l+tl+cl-1,k,l,mid,x*2);
    else
        return query(mid+1+tr,mid+1+tr+cr-1,k-cl,mid+1,r,x*2+1);
}
int cquery(int ql,int qr,int k,int l,int r,int x)//返回[ql,qr]小于等于k的数量
{
    if(l==r)
    {
        if(tree[x].num[1]<=k) return 1;
        else return 0;
    }
    int cl=tree[x].c[qr-l+1]-tree[x].c[ql-1-l+1];//[ql,qr]中有多少进入了左子树
    int cr=qr-ql+1-cl;//[ql,qr]中有多少进入了右子树
    int mid=(l+r)/2;
    int tl=tree[x].c[ql-1-l+1]-tree[x].c[l-1-l+1];//[l,ql-1]中有多少进入了左子树
    int tr=ql-1-l+1-tl;//[l,ql-1]中有多少进入了右子树
    if(sorted[mid]<=k)
        return cl+cquery(mid+1+tr,mid+1+tr+cr-1,k,mid+1,r,x*2+1);
    else return cquery(l+tl,l+tl+cl-1,k,l,mid,x*2);
}
int main()
{
    //freopen("in.txt","r",stdin);
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++)
            scanf("%d",&sorted[i]);
        init(1,n,1);//内存分配
        for(int i=1;i<=n;i++)
            tree[1].num[i]=sorted[i];
        sort(sorted+1,sorted+1+n);
        build(1,n,1);
        scanf("%d",&m);
        while(m--)
        {
            int a,b,c;
            scanf("%d %d %d",&a,&b,&c);
            printf("%d\n",query(a+1,b+1,b-a+1-c+1,1,n,1));
        }
    }
    return 0;
}

 

posted @ 2017-08-28 16:46  爱种树的码农  阅读(149)  评论(0编辑  收藏  举报