zoukankan      html  css  js  c++  java
  • 【专题】划分树>求区间第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;
    }
  • 相关阅读:
    Java 1.7.0_21b11 Code Execution
    nginx NULLByte 任意代码执行漏洞
    nginx ‘ngx_http_close_connection()’远程整数溢出漏洞
    WordPress WP Super Cache插件任意代码执行漏洞
    memcached 远程拒绝服务漏洞
    原环套原环
    要去哈尔滨了
    母亲节就要到了,你忘了吗?
    对于流媒体服务的一点概念
    有了螃蟹让心情好一点
  • 原文地址:https://www.cnblogs.com/markliu/p/2740086.html
Copyright © 2011-2022 走看看