K-th Number
两种写法:分块、线段树(归并树)
区间种第 k 个数的特点是:①在区间中不超过 x 的数不少于 k 个②在区间中小于 x 的数有不到 k 个
分块:
所以可以对答案进行二分,然后在判断是否满足的时候利用分块进行优化。
如果直接用\(\sqrt{n}\)作为桶的大小的话,复杂度可能有些高。如果把桶的数量设置得比桶内元素略少一些,可以使得程序更加高效。
// Created by CAD on 2020/2/9.
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn=1e5+5;
int a[maxn],cop[maxn];
const int maxb=1000;
vector<int> b[maxb];
int blo,belo[maxn];
bool judge(int l,int r,int x,int k){
int ans=0;
for(int i=l;i<=min(r,belo[l]*blo);++i)
if(x>=a[i]) ans++;
if(belo[l]!=belo[r]){
for(int i=(belo[r]-1)*blo+1;i<=r;++i)
if(x>=a[i]) ans++;
}
for(int i=belo[l]+1;i<=belo[r]-1;++i)
ans+=upper_bound(b[i].begin(),b[i].end(),x)-b[i].begin();
return ans>=k;
}
int main()
{
int n,m; scanf("%d%d",&n,&m);
blo=900;
for(int i=1;i<=n;++i){
belo[i]=(i-1)/blo+1;
scanf("%d",&a[i]);
b[belo[i]].push_back(a[i]);
cop[i]=a[i];
}
sort(cop+1,cop+1+n);
for(int i=1;i<=(n-1)/blo+1;++i)
sort(b[i].begin(),b[i].end());
while(m--){
int i,j,k; scanf("%d%d%d",&i,&j,&k);
int l=1,r=n,ans=cop[n];
while(l<=r){
int mid=(l+r)>>1;
if(judge(i,j,cop[mid],k)) ans=cop[mid],r=mid-1;
else l=mid+1;
}
printf("%d\n",ans);
}
return 0;
}
归并树:
此时线段树的每一个节点保存的不是数值。而是一个序列,节点的序列是其子树上的有序点集,利用merge()
函数的特性,保证了每一个节点所存序列的有序性。
// Created by CAD on 2020/2/10.
#include <cstdio>
#include <vector>
#include <algorithm>
#define lson (p<<1)
#define rson (p<<1|1)
using namespace std;
const int maxn=1e5+5;
int a[maxn],cop[maxn];
vector<int> d[maxn<<2];
void build(int s,int t,int p){
if(s==t){
d[p].push_back(a[s]);
return ;
}
int mid=(s+t)>>1;
build(s,mid,lson),build(mid+1,t,rson);
d[p].resize(t-s+1);
merge(d[lson].begin(),d[lson].end(),d[rson].begin(),d[rson].end(),d[p].begin());
}
int query(int l,int r,int x,int s,int t,int p){
if(l<=s&&t<=r)
return upper_bound(d[p].begin(),d[p].end(),x)-d[p].begin();
int mid=(s+t)>>1,cnt=0;
if(l<=mid) cnt+=query(l,r,x,s,mid,lson);
if(r>mid) cnt+=query(l,r,x,mid+1,t,rson);
return cnt;
}
int main()
{
int n,m; scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",&a[i]),cop[i]=a[i];
build(1,n,1);
sort(cop+1,cop+1+n);
while(m--){
int i,j,k; scanf("%d%d%d",&i,&j,&k);
int l=1,r=n,ans=cop[n];
while(l<=r){
int mid=(l+r)>>1;
if(query(i,j,cop[mid],1,n,1)>=k) ans=cop[mid],r=mid-1;
else l=mid+1;
}
printf("%d\n",ans);
}
return 0;
}
CAD加油!欢迎跟我一起讨论学习算法,QQ:1401650042