題意:給出n(<=100000)個數,m個詢問,詢問[L,R]區間內第k小數,兩兩查詢區間互不包含。
解:因爲兩兩查詢區間互不包含,所以按L從小到大排序,則必有Li<=Lj ,Ri<=Rj(0<i<j<=n)
所以對於每一個查詢,刪除當前treap中當前查詢不包含的點,插入當前treap中當前查詢未插入的點
每個點刪,插各一次,用treap實現,所以時間複雜度爲O(nlogn)
代碼:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<ctime>
#define L(i) T[i].s[0]
#define R(i) T[i].s[1]
using namespace std;
const int N=100011;
struct node{
int s[2],sz,pri,v;
void set(int x,int y){
sz=1;v=x;pri=y;
}
}T[N];
struct q{
int l,r,id,k;
bool operator < (const q a){
return a.l>l;
}
}s[N];
int a[N],rt,tot,ans[N];
inline void maintain(int i){T[i].sz=T[L(i)].sz+T[R(i)].sz+1;}//維護
inline void Rotate(int &y,int f){//旋轉
int x=T[y].s[!f];
T[y].s[!f]=T[x].s[f];
T[x].s[f]=y;
maintain(y);maintain(x);
y=x;
}
inline void insert(int &i,int val){
if (!i){
T[i=++tot].set(val,rand());
return;
}
bool f=T[i].v>val;
insert(T[i].s[!f],val);
if (T[T[i].s[!f]].pri>T[i].pri)Rotate(i,f);
else maintain(i);
}
inline void Delete(int &i,int val){
if (val>T[i].v)Delete(T[i].s[1],val);
else if(val<T[i].v)Delete(T[i].s[0],val);
else{
if (!L(i) && !R(i))i=0;
else if (!L(i))i=R(i);
else if (!R(i))i=L(i);
else{
int f=T[L(i)].pri>T[L(i)].pri;//若左子樹pri大於右子樹,將左子樹右旋至當前節點,則當前節點旋至右子樹,刪右子樹,反之亦然
Rotate(i,f);
Delete(T[i].s[f],val);
}
}
if (i)maintain(i);
}
inline int query(int i,int kth){
if (kth<=T[L(i)].sz)query(L(i),kth);
else if (kth==T[L(i)].sz+1)return T[i].v;
else query(R(i),kth-T[L(i)].sz-1);
}
int main(){
int n,m;
srand(time(0));
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)scanf("%d",&a[i]);
for (int i=1;i<=m;i++){
scanf("%d%d%d",&s[i].l,&s[i].r,&s[i].k);
if (s[i].l>s[i].r)swap(s[i].l,s[i].r);
s[i].id=i;
}
sort(s+1,s+m+1);
s[0].l=-1;s[0].r=-2;
for (int i=1;i<=m;i++){
for (int j=s[i-1].l;j<=min(s[i-1].r,s[i].l-1);j++)Delete(rt,a[j]);
for (int j=max(s[i].l,s[i-1].r+1);j<=s[i].r;j++)insert(rt,a[j]);
ans[s[i].id]=query(rt,s[i].k);
}
for (int i=1;i<=m;i++)printf("%d\n",ans[i]);
return 0;
}