划分树 模板


划分树——给出n个数,m个询问,每次询问区间[x,y]中第k小的数是多少。


在 [ 2 4 3 5 8 1 7 6 ] 中找区间[2,7]的第2小
①           [ 2 4 3 5 8 1 7 6 ]            // 原数组  [2,7]找第2小
②     [ 2 4 3 1 ]         [ 5 8 7 6 ]      // 分成左右两组  知道[2,7]中有3个到左子树  故在答案左
③  [ 2 1 ]   [ 4 3 ]    [ 5 6 ]  [ 8 7 ]   // 同理  只有1个到左子树  故答案在右子树
④  [1] [2]   [3] [4]    [5] [6]  [7] [8]   // 一直迭代直到找到答案


#include <iostream> 
#include <cstdio> 
#include <algorithm> 
using namespace std; 
   
#define N 100500 
   
#define MID ((l+r)>>1) 
int a[N],s[N],t[20][N],num[20][N],n,m; 
/*
建树:对于区间[l,r],首先通过对原数组的排序找到这个区间的中位数a[mid],
小于a[mid]的数划入他的左子树[l,mid-1],大于它的划入右子树[mid,r]。同时,
对于第i个数,记录在[l,i]区间内有多少数被划入左子树。最后,
对它的左子树区间[l,mid-1]和右子树区间[mid,r]递归的继续建树就可以了。
*/
//  void build(int lft,int rht,int ind)
//	{
//		if(lft==rht) return;
//
//		int mid=MID(lft,rht);
//		int same=mid-lft+1,ln=lft,rn=mid+1;
//		for(int i=lft;i<=rht;i++)
//			if(valu[ind][i]<order[mid]) same--;
//		for(int i=lft;i<=rht;i++)
//		{
//			int flag=0;
//			if((valu[ind][i]<order[mid])||(valu[ind][i]==order[mid]&&same))//划分到左区间
//			{
//				flag=1;
//				valu[ind+1][ln++]=valu[ind][i];
//				lsum[ind][i]=lsum[ind][i-1]+valu[ind][i];
//				if(valu[ind][i]==order[mid]) same--;
//			}
//			else //划分到右区间
//			{
//				valu[ind+1][rn++]=valu[ind][i];
//				lsum[ind][i]=lsum[ind][i-1];
//			}
//			num[ind][i]=num[ind][i-1]+flag;//划分到左区间的个数
//		}
//		build(lft,mid,ind+1);
//		build(mid+1,rht,ind+1);
//	}
//  int query(int st,int ed,int k,int lft,int rht,int ind)//子区间[st,ed],大区间[lft,rht],层数ind
//	{
//		if(lft==rht) return valu[ind][lft];
//
//		int mid=MID(lft,rht);
//		int lx=num[ind][st-1]-num[ind][lft-1];//在左区间不属于子区间的个数
//		int ly=num[ind][ed]-num[ind][st-1];//有多少个进入左区间
//		int rx=st-1-lft+1-lx;//在右区间不属于子区间的个数
//		int ry=ed-st+1-ly;//有多少个进入右区间
//		if(ly>=k) return query(lft+lx,lft+lx+ly-1,k,lft,mid,ind+1);
//		else 
//		{
//			isum+=lsum[ind][ed]-lsum[ind][st-1];
//			st=mid+1+rx;
//			ed=mid+1+rx+ry-1;
//			return query(st,ed,k-ly,mid+1,rht,ind+1);
//		}
//	}
void Build(int c,int l,int r) 
{ 
    int lm=MID-l+1,lp=l,rp=MID+1; 
    for(int i=l;i<=MID;i++) 
        lm-=s[i]<s[MID]; 
    for(int i=l;i<=r;i++) 
    { 
        if( i==l ) 
            num[c][i]=0; 
        else
            num[c][i]=num[c][i-1]; 
        if( t[c][i]==s[MID] ) 
        { 
            if( lm ) 
            { 
                lm--; 
                num[c][i]++; 
                t[c+1][lp++]=t[c][i]; 
            } 
            else
                t[c+1][rp++]=t[c][i]; 
        } 
        else if( t[c][i]<s[MID] ) 
        { 
            num[c][i]++; 
            t[c+1][lp++]=t[c][i]; 
        } 
        else
            t[c+1][rp++]=t[c][i]; 
    } 
    if( l<r ) 
        Build(c+1,l,MID),Build(c+1,MID+1,r); 
} 
   
int Query(int c,int l,int r,int ql,int qr,int k) 
{ 
    if( l==r ) 
        return t[c][l]; 
    int s,ss; 
    if( l==ql ) 
        s=0,ss=num[c][qr]; 
    else
        s=num[c][ql-1],ss=num[c][qr]-num[c][ql-1]; 
    if( k<=ss ) 
        return Query(c+1,l,MID,l+s,l+s+ss-1,k); 
    else
        return Query(c+1,MID+1,r,MID+1+ql-l-s,MID+1+qr-l-s-ss,k-ss); 
} 
   
int main() 
{ 
    scanf("%d%d",&n,&m); 
    for(int i=1;i<=n;i++) 
    { 
        scanf("%d",&a[i]); 
        s[i]=t[0][i]=a[i]; 
    } 
    sort(s+1,s+1+n); 
    Build(0,1,n); 
    while( m-- ) 
    { 
        int l,r,k; 
        scanf("%d%d%d",&l,&r,&k); 
        printf("%d\n",Query(0,1,n,l,r,k)); 
    } 
    return 0; 
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值