您的位置:首页 > 其它

pku2104 K-th Number

2008-11-15 00:53 274 查看
题意:给定一个序列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<=m && 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 最前

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: