Fudan University Local Contest 2012 题解(划分树)

G - The Famous ICPC Team Agai(hdu 4251)

题目要求:给出区间(区间长度为奇数odd),的中间值。用了划分树

http://blog.csdn.net/shiqi_614/article/details/8041390 划分树题解
http://blog.csdn.net/qq172108805/article/details/7761967 划分树题解


/*
求区间中间值
可以转化为求kth值  所以用了划分树
*/
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#define MAX 100009
using namespace std;
struct node {
    int num[MAX];//num记录元素所在区间的当前位置进入左子树的个数
    int val[MAX];//val记录第k层当前位置元素的值N
}t[40]; //(logn)
int n;
int srted[MAX];

void build(int l,int r,int d) { //建树  此树并非由节点构成 而是由数组的一段构成  如:d层的l~r是一个节点
    if(l==r) return;
    int mid=(l+r)>>1;//取中间那个
    int midd=srted[mid];
    int same=mid-l+1,samed=0,zn=l-1,yn=mid,i;//same初识化为左子树元素个数
    //下面减去比midd小的(一定会进入左孩子里边)  剩下的就是==midd并且要进入左孩子的个数
    //samed是已经插入的数目
    //zn、yn是左右孩子的开始位置-1,下面会把元素分到两个子树的区域里边
    for(i=l; i<=r; ++i)
        if(t[d].val[i]<midd) --same;
    for(i=l; i<=r; ++i) { //有点像快排  大的放到后边  小的放前边    相等的看情况
        if(i==l) t[d].num[i]=0;
        else t[d].num[i]=t[d].num[i-1];
        if(t[d].val[i]<midd) {
            ++t[d].num[i];//这里是统计从l到i有多少元素进入了左子树    这是划分树主要用到的数据
            t[d+1].val[++zn]=t[d].val[i];
        } else if(t[d].val[i]>midd) { //进入右子树
            t[d+1].val[++yn]=t[d].val[i];
        } else {
            if(samed<same) {//左子树还差same个  放左孩子里边
                ++samed;
                ++t[d].num[i];
                t[d+1].val[++zn]=t[d].val[i];
            } else //左子树已经放满,则放入右子树
                t[d+1].val[++yn]=t[d].val[i];
        }
    }
    build(l,mid,d+1);//建左右子树
    build(mid+1,r,d+1);
}

int query(int a,int b,int k,int l,int r,int d)//在d层[l,r]的节点里查找[a,b]中的第k大值
{
    if(a==b) return t[d].val[a];
    int mid=(l+r)>>1;
    int sx=t[d].num[a-1],sy=t[d].num[b];
    if(a-1<l) sx=0;
    if(sy-sx>=k)//[a,b]进入左子树的元素>=k,由于进入时是按顺序进入的,那么左子树的前sx个不在[a,b]之间
        return query(l+sx,l+sy-1,k,l,mid,d+1);
    else
    {
        int s1=(a==1?0:a-l-sx);//a前面进入右子树的元素的个数
        int s2=(b-a+1)-(sy-sx);//[a,b]之间进入右子树的元素的个数?
        int nk=k-(sy-sx);//前(sy-sx)大的元素在左子树里  剩下的在右子树里边找
        return query(mid+1+s1,mid+s1+s2,nk,mid+1,r,d+1);//mid->mid+s1之前的不是[a,b]之间的
    }
}


int main() {
    int cas=1;
    while(~scanf("%d",&n)){
        printf("Case %d:\n",cas++);
        for(int i=1;i<=n;i++){
              scanf("%d",&srted[i]);
              t[0].val[i]=srted[i];
        }
        sort(srted+1,srted+n+1);
        build(1,n,0);
        int m;
        scanf("%d",&m);
        int a,b;
        while(m--){
            scanf("%d %d",&a,&b);
            int ans=query(a,b,(a+b)/2-a+1,1,n,0);
            printf("%d\n",ans);
        }
    }
    return 0;
}
  

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值