划分树

老是忘 怕忘。。。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <string>
#include <vector>

using namespace std;

#define N 100500

#define MID ((l+r)>>1)
int a[N],s[N],t[20][N],num[20][N],n,m;

void Build(int c,int l,int r)
{
    int lm=MID-l+1,lp=l,rp=MID+1; //lm左长,lp左子左边,rp右子左边
    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);//(l+s到l+s+ss内有答案,左边小的不属于它)
    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;
}

 

posted on 2016-08-10 16:10  very_czy  阅读(124)  评论(0编辑  收藏  举报

导航