【专题】划分树>求区间第K大的数

hdu-4417 求区间不大于h的单位数

划分树模板+二分单位数(第k大的数)

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
const int MAXN = 100010;
int tree[30][MAXN];//表示每层每个位置的值
int sorted[MAXN];//已经排序的数
int toleft[30][MAXN];//toleft[p][i]表示第i层从1到i有多少个数分入左边
//long long sum[20][MAXN];

void build(int l,int r,int dep){
    if(l==r)  return ;
    /*if(l==r){
        sum[dep][l]=tree[dep][l];
        return ;
    }*/
    int mid = (l+r)/2;
    int same = mid-l+1;//表示等于中间值而且被分入左边的个数
    for(int i=l;i<=r;i++)
        if(tree[dep][i]<sorted[mid])
            same--;
    /*for(int i=l;i<=r;i++){
        if(tree[dep][i]<sorted[mid])
            same--;
        sum[dep][i]=tree[dep][i];
        if(i>l) sum[dep][i]+=sum[dep][i-1];
    }*/

    int lpos=l;
    int rpos=mid+1;
    for(int i=l;i<=r;i++){
        if(tree[dep][i]<sorted[mid])//比中间的数小,分入左边
            tree[dep+1][lpos++]=tree[dep][i];
        else if(tree[dep][i]==sorted[mid] && same>0){
            tree[dep+1][lpos++]=tree[dep][i];
            same--;
        }
        else{//比中间值大,分入右边
            tree[dep+1][rpos++]=tree[dep][i];
        }
        toleft[dep][i]=toleft[dep][l-1]+lpos-l;//从1到i放左边的个数

    }
    build(l,mid,dep+1);
    build(mid+1,r,dep+1);
}

//long long ans;
//查询区间第k大的数,[L,R]是大区间,[l,r]是要查询的小区间
int query(int L,int R,int l,int r,int dep,int k){
    if(l==r) return tree[dep][l];
    int mid=(L+R)/2;
    int cnt=toleft[dep][r]-toleft[dep][l-1];

    /*int ss=toleft[dep][l-1]-toleft[dep][L-1];
    int ee=l-L-ss;
    int s =toleft[dep][r]-toleft[dep][l-1];
    int e =r-l+1-s;*/

    if(cnt>=k){

        /*if(e>0){
            if(ee>0) ans+=sum[dep+1][mid+e+ee] - sum[dep+1][mid+ee];
            else ans+=sum[dep+1][mid+e];
        }*/


        //L+要查询的区间前被放在左边的个数
        int newl=L+toleft[dep][l-1]-toleft[dep][L-1];
        //左端点加上查询区间会被放在左边的个数
        int newr=newl+cnt-1;
        return query(L,mid,newl,newr,dep+1,k);
    }
    else{

        /*if(s>0){
            if(ss>0) ans-=sum[dep+1][L+s+ss-1] - sum[dep+1][L+ss-1];
            else ans-=sum[dep+1][L+s-1];
        }*/


        int newr=r+toleft[dep][R]-toleft[dep][r];
        int newl=newr-(r-l-cnt);
        return query(mid+1,R,newl,newr,dep+1,k-cnt);
    }
}
int solve(int n,int s,int t,int h){
    int l=1,r=t-s+1,mid,ans=0;
    while(l<=r){
        mid=(l+r)/2;
        int tmp=query(1,n,s,t,0,mid);
        if(tmp<=h){
            ans=mid;
            l=mid+1;
        }
        else{
            r=mid-1;
        }
    }
    return ans;
}
int main(){
    //freopen("in.txt","r",stdin);
    int T;
    int n,m;
    int s,t,k,h;
    scanf("%d",&T);
    int C=T;
    while(T--)
    {
        printf("Case %d:\n",C-T);
        scanf("%d%d",&n,&m);
        memset(tree,0,sizeof(tree));
        for(int i=1;i<=n;i++){
            scanf("%d",&tree[0][i]);
            sorted[i]=tree[0][i];
        }
        sort(sorted+1,sorted+n+1);
        build(1,n,0);
        while(m--){
            scanf("%d%d%d",&s,&t,&h);
            s++,t++;
            printf("%d\n",solve(n,s,t,h));
        }
    }
    return 0;
}

hdu-2665 求区间第K大的数

View Code
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
const int MAXN = 100010;
int tree[30][MAXN];//表示每层每个位置的值
int sorted[MAXN];//已经排序的数
int toleft[30][MAXN];//toleft[p][i]表示第i层从1到i有多少个数分入左边

void build(int l,int r,int dep){
    if(l==r)  return ;
    int mid = (l+r)/2;
    int same = mid-l+1;//表示等于中间值而且被分入左边的个数
    for(int i=l;i<=r;i++)
        if(tree[dep][i]<sorted[mid])
            same--;
    int lpos=l;
    int rpos=mid+1;
    for(int i=l;i<=r;i++){
        if(tree[dep][i]<sorted[mid])//比中间的数小,分入左边
            tree[dep+1][lpos++]=tree[dep][i];
        else if(tree[dep][i]==sorted[mid] && same>0){
            tree[dep+1][lpos++]=tree[dep][i];
            same--;
        }
        else{//比中间值大,分入右边
            tree[dep+1][rpos++]=tree[dep][i];
        }
        toleft[dep][i]=toleft[dep][l-1]+lpos-l;//从1到i放左边的个数

    }
    build(l,mid,dep+1);
    build(mid+1,r,dep+1);
}


//查询区间第k大的数,[L,R]是大区间,[l,r]是要查询的小区间
int query(int L,int R,int l,int r,int dep,int k){
    if(l==r) return tree[dep][l];
    int mid=(L+R)/2;
    int cnt=toleft[dep][r]-toleft[dep][l-1];
    if(cnt>=k){
        //L+要查询的区间前被放在左边的个数
        int newl=L+toleft[dep][l-1]-toleft[dep][L-1];
        //左端点加上查询区间会被放在左边的个数
        int newr=newl+cnt-1;
        return query(L,mid,newl,newr,dep+1,k);
    }
    else{
        int newr=r+toleft[dep][R]-toleft[dep][r];
        int newl=newr-(r-l-cnt);
        return query(mid+1,R,newl,newr,dep+1,k-cnt);
    }
}
int main(){
    int T;
    int n,m;
    int s,t,k;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        memset(tree,0,sizeof(tree));
        for(int i=1;i<=n;i++){
            scanf("%d",&tree[0][i]);
            sorted[i]=tree[0][i];
        }
        sort(sorted+1,sorted+n+1);
        build(1,n,0);
        while(m--){
            scanf("%d%d%d",&s,&t,&k);
            printf("%d\n",query(1,n,s,t,0,k));
        }
    }
    return 0;
}

hdu-4251 求区间中间值[k=(t-s)/2+1]

View Code
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
const int MAXN = 100010;
int tree[30][MAXN];//表示每层每个位置的值
int sorted[MAXN];//已经排序的数
int toleft[30][MAXN];//toleft[p][i]表示第i层从1到i有多少个数分入左边

void build(int l,int r,int dep){
    if(l==r)  return ;
    int mid = (l+r)/2;
    int same = mid-l+1;//表示等于中间值而且被分入左边的个数
    for(int i=l;i<=r;i++)
        if(tree[dep][i]<sorted[mid])
            same--;
    int lpos=l;
    int rpos=mid+1;
    for(int i=l;i<=r;i++){
        if(tree[dep][i]<sorted[mid])//比中间的数小,分入左边
            tree[dep+1][lpos++]=tree[dep][i];
        else if(tree[dep][i]==sorted[mid] && same>0){
            tree[dep+1][lpos++]=tree[dep][i];
            same--;
        }
        else{//比中间值大,分入右边
            tree[dep+1][rpos++]=tree[dep][i];
        }
        toleft[dep][i]=toleft[dep][l-1]+lpos-l;//从1到i放左边的个数

    }
    build(l,mid,dep+1);
    build(mid+1,r,dep+1);
}


//查询区间第k大的数,[L,R]是大区间,[l,r]是要查询的小区间
int query(int L,int R,int l,int r,int dep,int k){
    if(l==r) return tree[dep][l];
    int mid=(L+R)/2;
    int cnt=toleft[dep][r]-toleft[dep][l-1];
    if(cnt>=k){
        //L+要查询的区间前被放在左边的个数
        int newl=L+toleft[dep][l-1]-toleft[dep][L-1];
        //左端点加上查询区间会被放在左边的个数
        int newr=newl+cnt-1;
        return query(L,mid,newl,newr,dep+1,k);
    }
    else{
        int newr=r+toleft[dep][R]-toleft[dep][r];
        int newl=newr-(r-l-cnt);
        return query(mid+1,R,newl,newr,dep+1,k-cnt);
    }
}
int main(){
    int T;
    int n,m;
    int s,t,k;
    int C=0;
    while(scanf("%d",&n)!=EOF){
        printf("Case %d:\n",++C);
        memset(tree,0,sizeof(tree));
        for(int i=1;i<=n;i++){
            scanf("%d",&tree[0][i]);
            sorted[i]=tree[0][i];
        }
        sort(sorted+1,sorted+n+1);
        build(1,n,0);
        scanf("%d",&m);
        while(m--){
            scanf("%d%d",&s,&t);
            k=(t-s)/2+1;
            printf("%d\n",query(1,n,s,t,0,k));
        }
    }
    return 0;
}

hdu-3473 求区间中间值与区间其他值的差的绝对值之和。

需要理解并修改模板。

View Code
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
const int MAXN = 100010;
int tree[20][MAXN];//表示每层每个位置的值
int sorted[MAXN];//已经排序的数
int toleft[20][MAXN];//toleft[p][i]表示第i层从1到i有多少个数分入左边
long long sum[20][MAXN];

void build(int l,int r,int dep){
    //if(l==r)  return ;
    if(l==r){
        sum[dep][l]=tree[dep][l];
        return ;
    }
    int mid = (l+r)/2;
    int same = mid-l+1;//表示等于中间值而且被分入左边的个数
    /*for(int i=l;i<=r;i++)
        if(tree[dep][i]<sorted[mid])
            same--;*/
    for(int i=l;i<=r;i++){
        if(tree[dep][i]<sorted[mid])
            same--;
        sum[dep][i]=tree[dep][i];
        if(i>l) sum[dep][i]+=sum[dep][i-1];
    }

    int lpos=l;
    int rpos=mid+1;
    for(int i=l;i<=r;i++){
        if(tree[dep][i]<sorted[mid])//比中间的数小,分入左边
            tree[dep+1][lpos++]=tree[dep][i];
        else if(tree[dep][i]==sorted[mid] && same>0){
            tree[dep+1][lpos++]=tree[dep][i];
            same--;
        }
        else{//比中间值大,分入右边
            tree[dep+1][rpos++]=tree[dep][i];
        }
        toleft[dep][i]=toleft[dep][l-1]+lpos-l;//从1到i放左边的个数

    }
    build(l,mid,dep+1);
    build(mid+1,r,dep+1);
}

long long ans;
//查询区间第k大的数,[L,R]是大区间,[l,r]是要查询的小区间
int query(int L,int R,int l,int r,int dep,int k){
    if(l==r) return tree[dep][l];
    int mid=(L+R)/2;
    int cnt=toleft[dep][r]-toleft[dep][l-1];

    int ss=toleft[dep][l-1]-toleft[dep][L-1];
    int ee=l-L-ss;
    int s =toleft[dep][r]-toleft[dep][l-1];
    int e =r-l+1-s;

    if(cnt>=k){

        if(e>0){
            if(ee>0) ans+=sum[dep+1][mid+e+ee] - sum[dep+1][mid+ee];
            else ans+=sum[dep+1][mid+e];
        }


        //L+要查询的区间前被放在左边的个数
        int newl=L+toleft[dep][l-1]-toleft[dep][L-1];
        //左端点加上查询区间会被放在左边的个数
        int newr=newl+cnt-1;
        return query(L,mid,newl,newr,dep+1,k);
    }
    else{

        if(s>0){
            if(ss>0) ans-=sum[dep+1][L+s+ss-1] - sum[dep+1][L+ss-1];
            else ans-=sum[dep+1][L+s-1];
        }


        int newr=r+toleft[dep][R]-toleft[dep][r];
        int newl=newr-(r-l-cnt);
        return query(mid+1,R,newl,newr,dep+1,k-cnt);
    }
}
int main(){
    int T;
    int n,m;
    int s,t,k;
    scanf("%d",&T);
    int C=T;
    while(T--)
    {
        printf("Case #%d:\n",C-T);
        scanf("%d",&n);
        memset(tree,0,sizeof(tree));
        for(int i=1;i<=n;i++){
            scanf("%d",&tree[0][i]);
            sorted[i]=tree[0][i];
        }
        sort(sorted+1,sorted+n+1);
        build(1,n,0);
        scanf("%d",&m);
        while(m--){
            scanf("%d%d",&s,&t);
            s++,t++;
            k=(t-s)/2+1;
            ans=0;
            int mid=query(1,n,s,t,0,k);
            /*long long sum=0;
            for(int i=s;i<=t;i++){
                sum+=abs(tree[0][i]-mid);
            }*/
            if((t-s+1)%2==0){
                ans-=mid;
            }
            printf("%I64d\n",ans);
        }
        printf("\n");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/markliu/p/2740086.html