pku2104 K-th Number

题意:给定一个序列key[1..n]和m个询问{s,t,rank}(1 <= n <= 100 000, 1 <= m <= 5 000),对于每个询问输出区间[s,t]中第rank小的值

分析:由于2761和这题差不多,且数据量是这题的10倍,所以我一开始就把2761的SBT代码交上去,结果竟然是TLE,估计是栽在了"Case Time Limit: 2000MS"上面了。最终还是用了别人的思路,由此接触到一种很巧妙的结构:归并树

归并树可以用简单的一句话概括:利用类似线段树的树型结构记录合并排序的过程。

回顾一下如何利用归并树解决这道题:

1,建立归并树后我们得到了序列key[]的非降序排列,由于此时key[]内元素的rank是非递减的,因此key[]中属于指定区间[s,t]内的元素的rank也是非递减的,所以我们可以用二分法枚举key[]中的元素并求得它在[s,t]中的rank值,直到该rank值和询问中的rank值相等;
2,那对于key[]中的某个元素val,如何求得它在指定区间[s,t]中的rank?这就要利用到刚建好的归并树:我们可以利用类似线段树的query[s,t]操作找到所有属于[s,t]的子区间,然后累加val分别在这些子区间内的rank,得到的就是val在区间[s,t]中的rank,注意到这和合并排序的合并过程一致;
3,由于属于子区间的元素的排序结果已经记录下来,所以val在子区间内的rank可以通过二分法得到。

上面三步经过了三次二分操作(query也是种二分),于是每次询问的复杂度是O(log n * log n * log n)

PS:写二分查找时要注意细节。。

 

/*
Problem: 2104  User: zgmf_x20a 
Memory: 9708K  Time: 2454MS 
Language: C++  Result: Accepted 
*/
#include
<iostream>
#include 
<algorithm>
using namespace std;

#define MAXN 100000+5
#define LOGMAXN 17+5

int sortseq[LOGMAXN][MAXN],n,q,key[MAXN],s,t,rank;

struct Node{
    
int l,r;
}nod[
3*MAXN];

void buildtree(int u,int l,int r,int deep){
    
int i,j,m,loop;
    nod[u].l
=l;
    nod[u].r
=r;
    
if(l==r){
        sortseq[deep][l]
=key[l];
        
return;
    }
    m
=(l+r)/2;
    buildtree(
2*u,l,m,deep+1);
    buildtree(
2*u+1,m+1,r,deep+1);
    
    i
=l;j=m+1;loop=l;
    
while(i<=&& j<=r){
        
if(sortseq[deep+1][i]<sortseq[deep+1][j])
            sortseq[deep][loop
++]=sortseq[deep+1][i++];
        
else
            sortseq[deep][loop
++]=sortseq[deep+1][j++];
    }
    
if(i==m+1)
        
while(j<=r)
            sortseq[deep][loop
++]=sortseq[deep+1][j++];
    
else
        
while(i<=m)
            sortseq[deep][loop
++]=sortseq[deep+1][i++];
}


int query(int u,int val,int deep){
    
if(s<=nod[u].l && nod[u].r<=t)
        
return lower_bound(&sortseq[deep][nod[u].l],&sortseq[deep][nod[u].r]+1,val)-&sortseq[deep][nod[u].l];//*
    int res=0;
    
if(s<=nod[2*u].r)
        res
+=query(2*u,val,deep+1);
    
if(t>=nod[2*u+1].l)
        res
+=query(2*u+1,val,deep+1);
    
return res;
}


int main(){
    
int i,l,r,m,pos;
    
while(scanf("%d%d",&n,&q)!=EOF){
        
for(i=1;i<=n;i++)
            scanf(
"%d",&key[i]);
        buildtree(
1,1,n,1);
        
while(q--){
            scanf(
"%d%d%d",&s,&t,&rank);
            rank
--;//*
            l=1;r=n;
            
while(l<r){
                m
=(l+r+1)/2;//*
                pos=query(1,sortseq[1][m],1);
                
if(pos<=rank)
                    l
=m;
                
else
                    r
=m-1;
            }
            printf(
"%d\n",sortseq[1][l]);
        }
    }
    
return 0;
}

 

另附lower/upper_bound的用法:

    lower_bound(k)

p=c.lower_bound(k)

*p>=k p 最前

    upper_bound(k)

p=c.upper_bound(k)

*p>k p 最前

 

posted @ 2008-11-15 00:53  Beetlebum  阅读(1914)  评论(0编辑  收藏  举报