题目链接:点击打开链接
思路:treap树模板题, 可以动态维护一个有序表, 支持在O(logN)的时间内完成插入、删除一个元素和查找第K大元素的任务。 当然, treap树能做到的还远远不止这些, 常常与其他数据结构嵌套。
treap树是一种平衡二叉搜索树, 既满足堆的条件, 又满足排序二叉树的条件。
细节参见代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <bitset>
#include <cstdlib>
#include <cmath>
#include <set>
#include <list>
#include <deque>
#include <map>
#include <queue>
#include <ctime>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
typedef long double ld;
const ld eps = 1e-9, PI = 3.1415926535897932384626433832795;
const int mod = 1000000000 + 7;
const int INF = 0x3f3f3f3f;
const int seed = 131;
const ll INF64 = ll(1e18);
const int maxn = 3e4 + 10;
int T,n,m,a[maxn], cc, v;
struct treap {
int root, treapcnt, key[maxn],priority[maxn],
childs[maxn][2], cnt[maxn], _size[maxn];
treap() {
root = 0;
treapcnt = 1;
priority[0] = INF;
_size[0] = 0;
}
void update(int x) {
_size[x] = _size[childs[x][0]] + cnt[x] + _size[childs[x][1]];
}
void _rotate(int &x, int t) {
int y = childs[x][t]; //为了维持平衡而做的旋转操作,它并不直观
childs[x][t] = childs[y][1-t];
childs[y][1-t] = x;
update(x);
update(y);
x = y;
}
void _insert(int &x, int k) {
if(x) { //x == 0 表示该结点为空, 反之非空
if(key[x] == k) cnt[x]++; //cnt[x]表示结点x的值的个数,key[x]表示结点x的值
else {
int t = key[x] < k; //根据排序二叉树规则递归下去
_insert(childs[x][t], k);
if(priority[childs[x][t]] < priority[x]) { //根据堆排序规则
_rotate(x, t);
}
}
}
else {
x = treapcnt++; //创建新的结点
key[x] = k;
cnt[x] = 1;
priority[x] = rand(); //如果优先级是随机的,那么可以证明treap的期望深度是logN
childs[x][0] = childs[x][1] = 0; //0表示为空
}
update(x);
}
void _erase(int &x, int k) {
if(key[x] == k) {
if(cnt[x] > 1) --cnt[x];
else {
if(childs[x][0] == 0 && childs[x][1] == 0) {
x = 0; return ; //x设为0表示空
}
int t = priority[childs[x][0]] > priority[childs[x][1]];
_rotate(x, t); //直到把该结点旋转到叶子再删除
_erase(x, k);
}
}
else _erase(childs[x][key[x]<k], k);
update(x);
}
int _getKth(int &x, int k) {
if(k <= _size[childs[x][0]]) return _getKth(childs[x][0], k); //size[x]表示以x为根的子树的值得个数
k -= _size[childs[x][0]] + cnt[x]; //如果在右边,那么将要找右边的第k-size小的数
if(k <= 0) return key[x];
return _getKth(childs[x][1], k);
}
void insert(int k) {
_insert(root, k);
}
void erase(int k) {
_erase(root, k);
}
int getKth(int k) {
return _getKth(root, k);
}
};
int main() {
while(~scanf("%d%d",&n,&m)) {
treap g;
for(int i = 1; i <= n; i++) {
scanf("%d",&a[i]);
}
cc = 1;
for(int i = 1; i <= m; i++) {
scanf("%d",&v);
while(v >= cc) g.insert(a[cc++]);
printf("%d\n", g.getKth(i));
}
}
return 0;
}