題目描述
給定一個序列\(a_1,a_2,\dots,a_n\),\(m\)次操作,每次給定\(l,r,k\),問\(a_l,a_{l+1},\dots,a_r\)中第\(k\)小的值。
輸入
第一行一個正整數\(T(1\leq T\leq 3)\),表示測試資料的數量。
每組資料第一行\(n,m(1\leq n,m\leq 100000)\)。
第二行\(n\)個正整數\(a_1,a_2,\dots,a_n(1\leq a_i\leq n)\)。
接下來\(m\)行,每行描述一個操作,其中\(1\leq l\leq r\leq n,1\leq k\leq r-l+1\)。
輸出
對於每個詢問,輸出一行一個整數,即第\(k\)小的值。
樣例輸入 Copy
1
5 5
1 3 5 4 2
1 3 3
2 4 2
1 5 2
4 4 1
3 5 1
樣例輸出 Copy
5
4
2
4
2
考慮二分答案ans在[l,r]上的<=ans的數和k的關係,用f[i][j]表示前i個數裡j出現的次數,透過可持久化將j作為線段樹的點
然後考慮線段樹上二分去掉一個log
注意不能build,否則會超時,原因尚不明確
#pragma GCC optimize("O3,unroll-loops")
#pragma GCC target("avx2,bmi,bmi2,lzcnt,popcnt")
//如果在不支援 avx2 的平臺上將 avx2 換成 avx 或 SSE 之一
#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
typedef pair<int,int> PII;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;
typedef vector<string> VS;
typedef vector<int> VI;
typedef vector<vector<int>> VVI;
vector<int> vx;
inline int mp(int x) {return upper_bound(vx.begin(),vx.end(),x)-vx.begin();}
inline int log_2(int x) {return 31-__builtin_clz(x);}
inline int popcount(int x) {return __builtin_popcount(x);}
inline int lowbit(int x) {return x&-x;}
const int N = 1e5+10,M = 2e6;
//T[i]裡儲存的是i號線段樹的根節點標號,l,r,val要開到nlogn
int a[N],T[N],l[M],r[M],val[M];
int n,m,tot;
inline void pushup(int p)
{
val[p] = val[l[p]] + val[r[p]];
}
//對根為x的線段樹區間[a,b]裡的c點修改d
//注意change裡的左右節點編號
int change(int x,int a,int b,int c,int d)
{
int y = ++tot;
if(a==b)
{
val[y] = val[x] + d;
return y;
}
int M = (a+b)/2;
if(c<=M)
{
l[y] = change(l[x],a,M,c,d);
r[y] = r[x];
}
else
{
l[y] = l[x];
r[y] = change(r[x],M+1,b,c,d);
}
pushup(y);
//if(a==4&&b==4) cout<<l[y]<<' '<<r[y]<<'\n';
return y;
}
//對根為x當前區間為[a,b]的線段樹查詢區間[ql,qr]
int query(int x,int a,int b,int ql,int qr)
{
if(ql==a&&b==qr) return val[x];
int m = (a+b)/2;
//注意此處的修改用ql,qr表示詢問區間
if(qr<=m) return query(l[x],a,m,ql,qr);
else if(ql>m) return query(r[x],m+1,b,ql,qr);
else return query(l[x],a,m,ql,m)+query(r[x],m+1,b,m+1,qr);
}
//check(x)計算的是[l,r]中小於等於x的數字個數
//注意呼叫query時使用的是T陣列裡的標號
inline int ask(int x,int y,int k)
{
int a = 1,b = n;
while(a < b)
{
int m = (a+b)/2;
int t = val[l[y]] - val[l[x]];
if(t >= k) x = l[x],y = l[y],b = m;
else //注意若在右半邊需要把k-=t
k -= t, x = r[x], y = r[y], a = m+1;
}
return a;
}
void solve()
{
tot = 0;
cin>>n>>m;
for(int i=1;i<=n;++i) cin>>a[i];
// T[0] = 1;
// build(1,n);
for(int i=1;i<=n;++i) T[i] = change(T[i-1],1,n,a[i],1);
while(m--)
{
int l,r,k;
cin>>l>>r>>k;
cout<<ask(T[l-1],T[r],k)<<'\n';
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin>>T;
while(T--)
{
solve();
}
}