PAT1057 Stack(树状数组+倍增)
Posted acboyty
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PAT1057 Stack(树状数组+倍增)相关的知识,希望对你有一定的参考价值。
题目大意
要求维护一个栈,提供压栈、弹栈以及求栈内中位数的操作(当栈内元素(n)为偶数时,只是求第(n/2)个元素而非中间两数的平均值)。最多操作100000次,压栈的数字(key)范围是[1,100000]。
题目分析
前两个操作用(stack)就好。
求中位数。暴力做法即使用上优先队列也是稳稳的超时。考虑树状数组。
压栈时,将(key)值对应的位置加1。弹栈减1。
求中位数,可以二分求出(sum[1:p]==(n+1)/2)最小的(p),即为(ans)。复杂度(O(nlog^2n))。
问题已被解决,但是还有进一步优化的空间。
考虑倍增(?)。从高到低枚举(ans-1)的每一个二进制位,即求最大的(p)使得(sum[1:p]<(n+1)/2)。我们知道树状数组(tree[k]=sum_{i=k-lowbit(k)+1}^knum[i]),也就是说如果我们知道(sum_{i=1}^knum[i]=A)且((1<<j)<lowbit(k)),那么(sum_{i=1}^{k+(1<<j)}=A+tree[k+(1<<j)])。倍增的时候枚举二进制位的时候,恰巧我们也是从大到小枚举的,满足(j)与(k)的限制。这样,就将一次树状数组上(logn)的查询替换成一次简单的加法。复杂度(O(nlogn))。
#include <bits/stdc++.h>
using namespace std;
int num;
stack<int> st;
int sum[100005];
int lowbit(int x) {return x & -x;}
void add(int p, int v) {for (int i = p; i <= 100000; i += lowbit(i)) sum[i] += v;}
/*
int get(int p) {
int ret = 0;
for (int i = p; i >= 1; i -= lowbit(i)) ret += sum[i];
return ret;
}
*/
int main() {
num = 0;
while (!st.empty()) st.pop();
memset(sum, 0, sizeof(sum));
int n;
scanf("%d", &n);
for (int _ = 0; _ < n; ++_) {
char com[20];
scanf("%s", com);
if (strcmp(com, "Push") == 0) {
int key;
scanf("%d", &key);
++num;
st.push(key);
add(key, 1);
} else if (strcmp(com, "Pop") == 0) {
if (!st.empty()) {
int key = st.top();
printf("%d
", key);
--num;
st.pop();
add(key, -1);
} else printf("Invalid
");
} else {
if (!st.empty()) {
int temp = 0, ans = 0;
for (int i = 16; i >= 0; --i) {
if (ans + (1 << i) > 100000) continue;
if (temp + sum[ans + (1 << i)] < (num + 1) / 2) temp += sum[ans += (1 << i)];
}
printf("%d
", ans + 1);
} else printf("Invalid
");
}
}
return 0;
}
以上是关于PAT1057 Stack(树状数组+倍增)的主要内容,如果未能解决你的问题,请参考以下文章
PAT (Advanced Level) 1057 Stack