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 最前

 

你可能感兴趣的:(number)