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