//TREAP求第K小数 //感谢JSH大牛的指点 //这道题之所以可以用TREAP做是因为题意中有个条件,询问区间不会出现包含的情况,因此通过对询问区间进行排序 //然后通过插入和删除的方法,来维护区间的第K小 //平衡树的话自然首选TREAP了,TREAP中比较难的就在于删除那里,删除的方式是通过将要删除的结点通过左右旋的方式,在维护LEV为堆的同时,将要删除结点旋到叶子结点处,然后删除 #include<iostream> #include<cstdio> #include<algorithm> #include<ctime> using namespace std; const int MAX = 100005; struct Treap { int data,level,num; int l,r; }tree[MAX]; int size,root; void left(int &x)//注意这里是引用 { int p = tree[x].r; tree[x].r = tree[p].l;//注意这一行与下一行的顺序不能反 tree[p].l = x; x = p; tree[x].num = 1;//修改当前结点孩子个数 p = tree[x].l; if (p != 0)//左旋后必须更新左子树的信息 { tree[p].num = 1; if (tree[p].l != 0) tree[p].num += tree[tree[p].l].num; if (tree[p].r != 0) tree[p].num += tree[tree[p].r].num; tree[x].num += tree[p].num; } if(tree[x].r != 0) tree[x].num += tree[tree[x].r].num; } void right(int &x) { int p = tree[x].l; tree[x].l = tree[p].r; tree[p].r = x; x = p; tree[x].num = 1; p = tree[x].r; if(p != 0) { tree[p].num = 1; if(tree[p].l != 0) tree[p].num += tree[tree[p].l].num; if(tree[p].r != 0) tree[p].num += tree[tree[p].r].num; tree[x].num += tree[p].num; } if(tree[x].l != 0) tree[x].num += tree[tree[x].l].num; } void insert(int &x,int data) { if(x == 0) { x = ++size; tree[x].data = data; tree[x].l = tree[x].r = 0; tree[x].level = rand(); tree[x].num = 1; } else if(data < tree[x].data) { tree[x].num++; insert(tree[x].l,data); if(tree[x].level < tree[tree[x].l].level) right(x); } else { tree[x].num++; insert(tree[x].r,data); if(tree[x].level < tree[tree[x].r].level) left(x); } } void Remove(int &x,int data) { if(x == 0) return; if(data < tree[x].data) { tree[x].num--; Remove(tree[x].l,data); } else if(data > tree[x].data) { tree[x].num--; Remove(tree[x].r,data); } else { if(tree[x].l == 0 && tree[x].r == 0) x = 0;//此时无左右子树,到达叶子处了,将其删除 else if(tree[x].l == 0) x = tree[x].r; else if(tree[x].r == 0) x = tree[x].l; else { if(tree[tree[x].l].level > tree[tree[x].r].level) { right(x);//先旋转,把要删的结点不断选到叶子处,然后再删除 tree[x].num--;//每次旋转后X都会是当前子树的根 Remove(tree[x].r,data); } else { left(x); tree[x].num--; Remove(tree[x].l,data); } } } } int N,M,id; int A[MAX]; struct Query { int st,ed,id,k,ans; }Q[MAX]; bool cmp1(Query a,Query b) { if(a.st == b.st) return a.ed < b.ed; return a.st < b.st; } bool cmp2(Query a,Query b) { return a.id < b.id; } int Search(int x,int k) { int p = tree[x].r;//通过减去右子树的num来判断当前结点排第几位 int K = tree[x].num - (p != 0 ? tree[p].num : 0); if(K == k) return tree[x].data; else if(K > k) return Search(tree[x].l,k); else return Search(tree[x].r,k - K); } void solve() { root = size = 0; sort(Q+1,Q+id+1,cmp1); for(int i = 1;i <= M;++i) { if(i != 1) { for(int j = Q[i-1].st;j <= min(Q[i-1].ed,Q[i].st-1);++j) Remove(root,A[j]); } int j; if(i == 1) j = Q[i].st; else j = max(Q[i-1].ed + 1,Q[i].st); for(;j <= Q[i].ed;++j) insert(root,A[j]); Q[i].ans = Search(root,Q[i].k); } sort(Q+1,Q+id+1,cmp2); for(int i = 1;i <= M;++i) printf("%d/n",Q[i].ans); } int main() { srand(time(0)); //freopen("in.txt","r",stdin); int st,ed,k; 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",&st,&ed,&k); if (st > ed) swap(st,ed); Q[i].st = st; Q[i].ed = ed; Q[i].k = k; Q[i].id = i; } solve(); }