L3-002. 堆栈
时间限制
200 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
陈越
大家都知道“堆栈”是一种“先进后出”的线性结构,基本操作有“入栈”(将新元素插入栈顶)和“出栈”(将栈顶元素的值返回并从堆栈中将其删除)。现请你实现一种特殊的堆栈,它多了一种操作叫“查中值”,即返回堆栈中所有元素的中值。对于N个元素,若N是偶数,则中值定义为第N/2个最小元;若N是奇数,则中值定义为第(N+1)/2个最小元。
输入格式:
输入第一行给出正整数N(<= 105)。随后N行,每行给出一个操作指令,为下列3种指令之一:
Push keyPop
PeekMedian
其中Push表示入栈,key是不超过105的正整数;Pop表示出栈;PeekMedian表示查中值。
输出格式:
对每个入栈指令,将key入栈,并不输出任何信息。对每个出栈或查中值的指令,在一行中打印相应的返回结果。若指令非法,就打印“Invalid”。
输入样例:17 Pop PeekMedian Push 3 PeekMedian Push 2 PeekMedian Push 1 PeekMedian Pop Pop Push 5 Push 4 PeekMedian Pop Pop Pop Pop输出样例:
Invalid Invalid 3 2 2 1 2 4 4 5 3 Invalid
// 线段树求区域第K大值 #include <iostream> #include <stack> #include <string> using namespace std; struct node { int left, right, value; }tree[4 * 100005]; // i为当前操作的树节点的坐标,left,right为区间的范围。 void build(int i, int left , int right) { tree[i].left = left; tree[i].right = right; tree[i].value = 0; if (tree[i].left == tree[i].right) return; int mid = (tree[i].left + tree[i].right) / 2; build(i * 2, left, mid); build(i * 2 + 1, mid + 1, right); } // i为当前操作的树节点的坐标,left,rgiht为区间的范围,pos为出栈的值,这里的value为1就是push,-1为pop; void update(int i, int left, int right, int pos, int value) { if (tree[i].left == tree[i].right && tree[i].left == pos) { tree[i].value += value; return; } int mid = (tree[i].left + tree[i].right) / 2; if (mid >= pos) update(i * 2, left, mid, pos,value); else update(i * 2 + 1, mid + 1, right, pos,value); tree[i].value = tree[i * 2].value + tree[i * 2 + 1].value; } // i为当前操作的树节点的坐标,left,rgiht为区间的范围,pos为出栈的值 // 这里查询第(N/2)或(N+1)/2 大的值 int query(int i, int left, int right, int pos) { if (tree[i].left == tree[i].right) return tree[i].left; int mid = (tree[i].left + tree[i].right) / 2; if (tree[i * 2].value >= pos) query(i * 2, left, mid, pos); else // 如果查询的范围大于现在的范围了,需要把现在右边的范围里的值减掉 query(i * 2 + 1, mid + 1, right, pos - tree[i * 2].value); } int main() { //freopen("data.txt", "r", stdin); stack<int> sta; int N; string str; cin >> N; // 先将线段树初始化 build(1, 0, 100005); for (int i = 0; i < N; i++) { int num; cin >> str; if (str == "Push") { cin >> num; sta.push(num); update(1, 0, 100005, num, 1); } else if (str == "Pop") if (sta.empty()) cout << "Invalid" << endl; else { cout << sta.top() << endl; update(1, 0, 100005, sta.top(), -1); sta.pop(); } else { if (sta.empty()) cout << "Invalid" << endl; else { int n = sta.size(); if (n % 2 == 0) n /= 2; else n = (n + 1) / 2; cout << query(1, 0, 100005, n) << endl; } } } return 0; }
// 树状数组+二分查找 // getsum(x)的含义就是栈中不超过x的数有多少个 #include <iostream> #include <stack> #include <string> #define max 100005 using namespace std; int C[max] = { 0 }; // 树状数组的标准模版 int lowbit(int t) { return t & (-t); } void update(int pos, int value) { for (int i = pos; i < max; i += lowbit(i)) C[i] += value; } int getsum(int x) { int sum = 0; for (int i = x; i > 0; i -= lowbit(i)) sum += C[i]; return sum; } int main() { //freopen("data.txt", "r", stdin); stack<int> sta; int N; string str; cin >> N; for (int i = 0; i < N; i++) { int num; cin >> str; if (str == "Push") { cin >> num; sta.push(num); update(num, 1); } else if (str == "Pop") if (sta.empty()) cout << "Invalid" << endl; else { cout << sta.top() << endl; update(sta.top(), -1); sta.pop(); } else { if (sta.empty()) cout << "Invalid" << endl; else { int n = sta.size(); if (n % 2 == 0) n /= 2; else n = (n + 1) / 2; // 这里用二分查找 int left = 0, right = max; while (left < right) { int mid = (right + left) / 2; if (getsum(mid) >= n) right = mid; else left = mid + 1; } cout << left << endl; } } } return 0; }