hdu 2665 Kth number(划分树模板题)

来源:互联网 发布:iphone8爆炸知乎 编辑:程序博客网 时间:2024/06/05 10:14

题意:给定一个长度为n的序列,进行m次查询,求出区间[l,r]中的第k大值。

思路:划分树模板题。上学的时候做过这道题,当时看了下划分树的讲解,看得很头大,然后就一直放着了。十一回家的时候在高铁上没什么事情,就重新学习了一遍划分树。

划分树是通过模拟快速排序,记录快速排序的过程。因为快速排序本质上就是讲一个很大的序列划分成一段一段的小序列分别排序,所以可以将这个排序的过程给记录下来,然后根据排序的过程,逐步缩小查询区间。

思路讲起来很简单,但是实现的时候很麻烦。首先是区间是怎么缩小的,因为每次[l,r]区间内的元素都会根据每次排序的坐标值划分为两个部分,一部分在左子序列,一部分在右子序列,第k大值一定在其中某一边,所以可以排除另一边,然后根据排除情况调整l,r和k的值,直到区间中只剩下一个元素;其次是怎么保证树的深度,因为在最恶劣情况下快速排序的复杂度是O(n^2),树的深度为n,为了避免这种情况,我们可以先对原序列排序,反正我们需要的是排序的过程,至于排序的结果并不重要,我们可以通过排序的结果倒推出每次切割序列最完美的坐标值,保证树的深度为log2(n);最后是区间缩小的公式,开始的时候我以为这部分是最容易实现的,拿着笔画画算算推出公式就行了,但是实际上这个公式我推了很多遍到最后推出来的还是错的,还是根据测试数据不停地调整最后才AC掉了题目。

为了保证查询的效率,在编码的过程中还需要很多小技巧,具体可以看代码。

在学习划分树的过程中参考了百度百科。

最近工作略微轻松,可以有时间做一些想做的事情,所以花了比较多的时间看书和学习一些以前想学但是没腾出手来学的东西。

#include<stdio.h>#include<string.h>#include<stdlib.h>#define N 100005#define M 20int n,m;int origin[N],sort[N],sort_step[M][N],move_to_left[M][N];int cmp(const void *a,const void *b){    return *(int *)a-*(int *)b;}void Build(int rank,int l,int r){    if(l==r)    {        sort_step[rank][l]=sort_step[rank-1][l];        return ;    }    int mid=(l+r)/2;    int left_index=l,right_index=mid+1;    int left_seats=mid-l+1;    for(int i=l;i<=mid;i++)        if(sort[i]<sort[mid]) left_seats--;    for(int i=l;i<=r;i++)    {        if(i==l) move_to_left[rank][i]=0;        else move_to_left[rank][i]=move_to_left[rank][i-1];        if(sort_step[rank-1][i]<sort[mid]) sort_step[rank][left_index++]=sort_step[rank-1][i],move_to_left[rank][i]++;        else if(sort_step[rank-1][i]>sort[mid]) sort_step[rank][right_index++]=sort_step[rank-1][i];        else        {            if(left_seats) left_seats--,sort_step[rank][left_index++]=sort_step[rank-1][i],move_to_left[rank][i]++;            else sort_step[rank][right_index++]=sort_step[rank-1][i];        }    }    Build(rank+1,l,mid);    Build(rank+1,mid+1,r);    return ;}int Query(int rank,int l,int r,int query_l,int query_r,int k){    if(l==r) return sort_step[rank][l];    int mid=(l+r)/2;    int move_to_left_l,move_to_left_seg;    if(query_l==l) move_to_left_l=0,move_to_left_seg=move_to_left[rank][query_r];    else move_to_left_l=move_to_left[rank][query_l-1],move_to_left_seg=move_to_left[rank][query_r]-move_to_left[rank][query_l-1];    if(k<=move_to_left_seg) return Query(rank+1,l,mid,l+move_to_left_l,l+move_to_left[rank][query_r]-1,k);    else return Query(rank+1,mid+1,r,move_to_left[rank][r]+query_l-move_to_left_l,r-((r-query_r)-(move_to_left[rank][r]-move_to_left[rank][query_r])),k-move_to_left_seg);}int main(){    int T;    scanf("%d",&T);    while(T--)    {        scanf("%d%d",&n,&m);        for(int i=1;i<=n;i++)        {            scanf("%d",&origin[i]);            sort[i]=sort_step[0][i]=origin[i];        }        qsort(sort+1,n,sizeof(sort[0]),cmp);        Build(1,1,n);        while(m--)        {            int l,r,k;            scanf("%d%d%d",&l,&r,&k);            printf("%d\n",Query(1,1,n,l,r,k));        }    }    return 0;}


0 0