有一个数列a[1~N]中 (数字各不相同),输入m行i,j,k,目的是求a[i...j]之间第K小的数
思路:二分+线段树:二分枚举数字num,然后在线段树的每个区间中找小于num的个数c,根据c待定二分边界
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
ll n,m,a[N];
vector<ll> t[N];
void pushup(int k) {
merge(t[k<<1].begin(), t[k<<1].end(), t[k<<1|1].begin(), t[k<<1|1].end(), t[k].begin());
}
void build(int l, int r, int k) {
if (l==r) {
t[k].push_back(a[l]);
return;
}
int m=l+r>>1;
build(l,m,k<<1);
build(m+1,r,k<<1|1);
t[k].resize(r-l+1);
pushup(k);
}
int ask(int ql, int qr, int l, int r, int k, ll num) { //求ql,qr中找有多少个数字小于num
if (ql<=l && r<=qr) {
return upper_bound(t[k].begin(), t[k].end(), num)-t[k].begin(); //大于num的第一个位置,能到num
}
int m=l+r>>1, ans=0;
if (m>=ql) ans+=ask(ql,qr,l,m,k<<1,num);
if (m<qr) ans+=ask(ql,qr,m+1,r,k<<1|1,num);
return ans;
}
ll b_search(int s, int e, int k) {
ll l=-1e9, r=1e9+1;
while (l<r) {
ll m=l+r>>1;
if (ask(s,e,1,n,1,m)<k) l=m+1;
else r=m;
}
return l;
}
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n>>m; for (int i=1; i<=n; i++) cin>>a[i];
build(1,n,1);
for (int i=0; i<m; i++) {
int l,r,k; cin>>l>>r>>k;
cout<<b_search(l,r,k)<<'
';
}
return 0;
}
建树复杂度:遍历了logn层,每层的merge函数累积遍历n个元素
查询复杂度:mloglog(e-s)
感觉比按双字段排序还要慢一点...