本题是利用了归并排序的过程,其实归并树就是线段树+归并,只是线段树每个区间里存了这个区间里的有序序列,注意rank的求法,二分答案,求出每个中间值mid在原始序列区间[s,t]里的rank,其中这里面又可以用二分来求mid在[s,t]里排第几,最后相加就是这个rank,比较蛋疼的就是二分时的位置问题,什么时候+1,什么时候-1,很难搞!
每次询问的复杂度O(lgn*lgn*lgn),所以总共为O(m*lgn*lgn*lgn)
ps:归并树原理:
如果对于一段区间,仅查找一次第k大元素的话好说,直接一个快排搞定。
如果有多次离线询问,然后就可以通过归并排序,建一棵归并树(nlogn)对于树的每一个节点,通过归并排序递归的建立一个序列,其中每个节点[l,r]表示原序列中,[l,r]这些数字排序以后的状态。如图,红色节点表示会被分到左子树。
就这样,在区间[l,r]查找第k大元素的时候,先二分枚举元素x,求出x为第几大元素,再在归并树里查找相应区间,对于每一个被包含的区间,二分查找有多少个比当前枚举的元素x小,有多少元素小于等于当前枚举元素x,如果k刚好在这两段区间里,x就是第k大数。程序巨猥琐于是我没写- -!总体复杂度n(logn)^3……强烈膜拜想出此算法的。
这题竟然可以sort水过~震惊了!
#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <string.h>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <limits.h>
using namespace std;
int lowbit(int t){return t&(-t);}
int countbit(int t){return (t==0)?0:(1+countbit(t&(t-1)));}
int gcd(int a,int b){return (b==0)?a:gcd(b,a%b);}
#define LL long long
#define PI acos(-1.0)
#define N 100001
#define MAX INT_MAX
#define MIN INT_MIN
#define eps 1e-8
#define FRE freopen("a.txt","r",stdin)
int n,m;
int mertree[30][N];
int a[N];
struct node
{
int l,r;
};
node tree[N*3];
void Build(int i,int s,int t,int dep){//自下而上建归并树
tree[i].l=s;
tree[i].r=t;
if(s==t){
mertree[dep][s]=a[s];
return ;
}
int mid=(s+t)>>1;
Build(2*i,s,mid,dep+1);
Build(2*i+1,mid+1,t,dep+1);
int l=s,r=mid+1;
int cnt=s;
while(l<=mid && r<=t){
if(mertree[dep+1][l]<mertree[dep+1][r])
mertree[dep][cnt++]=mertree[dep+1][l++];
else
mertree[dep][cnt++]=mertree[dep+1][r++];
}
if(l==mid+1)
while(r<=t)
mertree[dep][cnt++]=mertree[dep+1][r++];
else
while(l<=mid)
mertree[dep][cnt++]=mertree[dep+1][l++];
}
int solve(int i,int s,int t,int val,int dep){ //O(lgn)
if(s<=tree[i].l && t>=tree[i].r){
int l=tree[i].l,r=tree[i].r;
int pos=lower_bound(&mertree[dep][l], &mertree[dep][r]+1, val)-&mertree[dep][l];//s,t子区间是已经排好序的,这是也要二分计算当前val在这个子区间里的rank,最后相加就是val在s,t区间里的rank------------O(lgn)
return pos;
}
int res=0;
if(s<=tree[2*i].r)
res+=solve(2*i,s,t,val,dep+1);
if(t>=tree[2*i+1].l)
res+=solve(2*i+1,s,t,val,dep+1);
return res;
}
int main(){
while(scanf("%d%d",&n,&m)!=EOF){
int i,j;
for(i=1;i<=n;i++)scanf("%d",&a[i]);
Build(1,1,n,1);
while(m--){
int s,t,rank;
scanf("%d%d%d",&s,&t,&rank);
rank--;// !!!!!
int l=1,r=n,mid;
while(l<r){ //O(lgn)
mid=(l+r+1)>>1;
int tmp=solve(1,s,t,mertree[1][mid],1);//mertree[1]是已经排好序的序列,用二分找出当前要查询的rank
if(tmp<=rank)l=mid;
else
r=mid-1;
}
printf("%d\n",mertree[1][l]);
}
}
return 0;
}
sort~~:
#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <string.h>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <limits.h>
using namespace std;
int lowbit(int t){return t&(-t);}
int countbit(int t){return (t==0)?0:(1+countbit(t&(t-1)));}
int gcd(int a,int b){return (b==0)?a:gcd(b,a%b);}
#define LL long long
#define PI acos(-1.0)
#define N 100001
#define MAX INT_MAX
#define MIN INT_MIN
#define eps 1e-8
#define FRE freopen("a.txt","r",stdin)
struct node
{
int pos;
int val;
}a[N];
bool cmp(node x,node y){
return x.val<y.val;
}
int main(){
int n,m;
while(scanf("%d%d",&n,&m)!=EOF){
int i,j,k;
for(i=1;i<=n;i++)
{
scanf("%d",&a[i].val);
a[i].pos=i;
}
sort(a+1,a+n+1,cmp);
while(m--){
int s,t,rank;
scanf("%d%d%d",&s,&t,&rank);
int cnt=0;
for(i=1;i<=n;i++){
if(s<=a[i].pos && a[i].pos<=t)
cnt++;
if(cnt==rank)break;
}
printf("%d\n",a[i].val);
}
}
return 0;
}