http://poj.org/problem?id=1442
给定n和m,表示有n个数往集合中插入,和m个输出。接下来是要插入的n个数,再然后是m个查询,对于某个查询b[i],意思是等到往集合中插入b[i]个数后查询第i小的数
用treap直接一个一个插入就好,然后直接查询第i小,这是平衡树的看家本领,代码如下:
#include
#include
#include
#include
using namespace std;
const int N = 50000 + 10;
int a[N], b[N];
struct node
{
int val, pri, son[2]; //pri是随机赋的优先级
int sz;
void init(int _val, int _pri, int _sz)
{
val = _val, pri = _pri, sz = _sz;
son[0] = son[1] = 0;
}
};
struct Treap
{
int tot, root; //tot用来对结点计数,root是treap的根
node tr[N];
void init()
{
tot = 0, root = 0;
}
void update(int x)
{
tr[x].sz = tr[tr[x].son[0]].sz + tr[tr[x].son[1]].sz + 1;
}
void rotate(int &x, int p) //旋转,分左旋和右旋,p=0为左旋,p=1为右旋
{
int y = tr[x].son[!p];
tr[x].son[!p] = tr[y].son[p];
tr[y].son[p] = x;
update(x); update(y);
x = y;
}
//插入,根据要插入的值确定要插入当前的左右子树,插入完成后根据pri检查是否满足堆的性质,不满足的话进行旋转操作
void insert(int &x, int val)
{
if(x == 0) tr[x = ++tot].init(val, rand(), 1);
else
{
tr[x].sz++;
int p = val > tr[x].val;
insert(tr[x].son[p], val);
if(tr[x].pri < tr[tr[x].son[p]].pri) rotate(x, !p);
}
}
int kth(int x, int k) //寻找树中的第k小值
{
if(k == tr[tr[x].son[0]].sz + 1) return tr[x].val;
if(k > tr[tr[x].son[0]].sz + 1) return kth(tr[x].son[1], k - tr[tr[x].son[0]].sz - 1);
else return kth(tr[x].son[0], k);
}
}treap;
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
treap.init();
b[0] = 0;
for(int i = 1; i <= m; i++)
{
scanf("%d", &b[i]);
for(int j = b[i-1]+1; j <= b[i]; j++)
treap.insert(treap.root, a[j]);
printf("%d\n", treap.kth(treap.root, i));
}
return 0;
}
用树状数组的话,首先离散化数据,然后依次插入,维护前缀和,二分求第i小值
#include
#include
#include
#include
#include
using namespace std;
const int N = 50000 + 10;
struct BIT
{
int n, b[N];
void init(int _n)
{
n = _n;
memset(b, 0, sizeof b);
}
void add(int i, int x)
{
for(; i <= n; i += i & -i) b[i] += x;
}
int sum(int i)
{
int ans = 0;
for(; i >= 1; i -= i & -i) ans += b[i];
return ans;
}
}bit;
int a[N], b[N], c[N];
int main()
{
int n, m;
while(~ scanf("%d%d", &n, &m))
{
bit.init(n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
memcpy(c, a, sizeof a);
sort(c + 1, c + 1 + n);
for(int i = 1; i <= n; i++) a[i] = lower_bound(c + 1, c + 1 + n, a[i]) - c;
b[0] = 0;
int k = 0;
for(int i = 1; i <= m; i++)
{
scanf("%d", &b[i]);
for(int j = b[i-1] + 1; j <= b[i]; j++) bit.add(a[j], 1);
++k;
int l = 1, r = n, res;
while(l <= r)
{
int mid = (l + r) >> 1;
if(bit.sum(mid) >= k) res = mid, r = mid - 1;
else l = mid + 1;
}
printf("%d\n", c[res]);
}
}
return 0;
}
用优先队列也可以做,核心思想维护建两个优先队列,一个从小到大出队(队列1),一个从大到小出队(队列2),维护队列2中元素个数为i-1个,且为前i-1小,那么队列1中的队首就是第i小
#include
#include
#include
#include
#include
using namespace std;
const int N = 50000 + 10;
int a[N], b[N];
int main()
{
int n, m;
while(~ scanf("%d%d", &n, &m))
{
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
b[0] = 0;
priority_queue<int, vector<int>, greater<int> > que1;
priority_queue<int, vector<int>, less<int> > que2;
for(int i = 1; i <= m; i++)
{
scanf("%d", &b[i]);
for(int j = b[i-1] + 1; j <= b[i]; j++)
{
que1.push(a[j]);
while(! que2.empty() && que1.top() < que2.top())
{
int tmp = que1.top(); que1.pop();
que1.push(que2.top()); que2.pop();
que2.push(tmp);
}
}
printf("%d\n", que1.top());
que2.push(que1.top()); que1.pop();
}
}
return 0;
}