您的位置:首页 > 其它

hdu 2655 (求区间K小数)

2013-08-18 20:35 155 查看
题目

划分树吧,在这学得

记没排序的数组为UA(unsorted array,即原数组),排序数组SA(sorted array)

划分树的建树过程:将区间为[L,R]的UA,按SA[MID]来划分,如果比SA[MID]值小,则分到左子树,比其大,则分到右子树,并把在某个位置小于等于SA[MID]的个数记录下来,直到区间长度为1为止。



Query函数在做的一个工作将区间长度逐渐缩为1。以下以在[L,R]内找第CNT大的数为例说明。假设

S表示在[L,R]内有几个小于等于DATA[MID]的个数

SS表示在[tr[root].l,l-1]内有几个小于等于DATA[MID]的个数



当S>=CNT时,将L变为tr[root].l+SS,因为现在的S已经比CNT大了,还要SS那部分有什么用,

而将R变为tr[root].l+ss+s-1则相当于将下一次的S值减少1,以使S趋向于CNT

示意图:





(注意:SS的值一定<=L-tr[root].l)





当S<CNT时,将L变为mid+(l-tr[root].l-ss)+1,括号内的数是为了进一步缩小区间长度用的。因为在[tr[root].l , l-1 ]内比DATA[MID]大的数,在下一步显然不需要

将R变为mid+l-tr[root].l-ss+(r-l+1-s),括号内的数是为了确定新区间的长度。即在[L,R]内有几个比DATA[MID]大的数。

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

#define N 100010
int sa
,tree[20]
,sum[20]
;
//sum[i][j]表示i层,1~j里比sa[m]小的数
int n,m,t,x,y,k;
struct node
{
    int l,r;
}root[N*4];
inline void build(int t,int d,int x,int y)
{
    root[t].l=x;
    root[t].r=y;
    if(x==y) return;
    int m=(x+y)>>1;
    int lless=m-x+1;
    for(int i=x;i<=y;i++)
    {
        if(tree[d][i]<sa[m]) lless--;
    }
    int lp=x,rp=m+1;
    for(int i=x;i<=y;i++)
    {
        if(i==x) sum[d][i]=0;
        else     sum[d][i]=sum[d][i-1];
        if(tree[d][i]<sa[m])
        {
            sum[d][i]++;
            tree[d+1][lp++]=tree[d][i];
        }
        else if(tree[d][i]>sa[m])
        {
            tree[d+1][rp++]=tree[d][i];
        }
        else
        {
            if(lless>0)
            {
                sum[d][i]++;
                lless--;
                tree[d+1][lp++]=tree[d][i];
            }
            else
            {
                tree[d+1][rp++]=tree[d][i];
            }
        }
    }
    build(t*2,d+1,x,m);
    build(t*2+1,d+1,m+1,y);
} 

inline int query(int t,int d,int x,int y,int k)
{
    if(x==y) return tree[d][x];
    int ss,s;
    int l=root[t].l,r=root[t].r,m=(l+r)>>1;
    //ss:[l,x)之间被分到左子树的,s:[x,y]被分到左子树的
    if(l==x)
    {
        ss=0;
        s=sum[d][y];
    }
    else
    {
        ss=sum[d][x-1];
        s=sum[d][y]-sum[d][x-1];
    }
    if(s>=k)
    {
        int xx=l+ss;//既然[x,y]的就够了,[l,x-1]就不用了里的ss个就没用了。
        int yy=l+ss+s-1;//将下一次的s减1
        return query(t*2,d+1,xx,yy,k);
    }
    else
    {
        int r1=x-l-ss;//[l,x-1]之间被分到右子树
        int r2=y-x+1-s;//[x,y]之间被分到右子树
        int xx=m+1 +r1;
        int yy=m+1 +r1+r2-1;
        return query(t*2+1,d+1,xx,yy,k-s);
    }
}

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        memset(sum,0,sizeof(sum));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&sa[i]);
            tree[0][i]=sa[i];
        }
        sort(sa+1,sa+n+1);
        build(1,0,1,n);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&x,&y,&k);
            printf("%d\n",query(1,0,x,y,k));
        }
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: