不删除双指针(栈模拟队列)
yukimianyan · · 算法·理论
队列
先给出一个问题描述,我们有一个二元函数
在这样的定义下,对于一个
双指针维护两个指针
双指针一般是用来尝试解决关于区间的问题的,
双栈模拟队列
如果信息不支持删除,但是有结合律且可以合并(例子:群,包括线性基),我们可以尝试双栈模拟队列。我们需要两个栈
每次加入一个元素的时候,往
由于每个元素只会从
template <class T>
struct queue1 {
T a[410], b[410], suf;
int topa, topb;
void clear() {
topa = topb = 0;
}
void push(const T& x) {
if (topa) suf = suf + x, a[++topa] = x;
else suf = a[++topa] = x;
}
void pop() {
if (topb) return --topb, void();
assert(topa);
swap(topa, topb);
b[1] = a[topb];
for (int i = 2; i <= topb; i++) b[i] = a[topb - i + 1] + b[i - 1];
--topb;
}
T sum() {
if (!topa && !topb) return /*零*/;
if (!topa) return b[topb];
if (!topb) return suf;
return b[topb] + suf;
}
};
单栈模拟队列
【UR #23】地铁规划 - 题目 - Universal Online Judge
如果信息不支持删除,但是支持加入和撤销最近一次操作,同时信息之间有交换律(可能还需要结合律)(例子:可撤销并查集),那么可以花费一个额外的
每次加入元素,就将其作为
但是这样复杂度肯定是错的,
为什么这样就对了呢?我们首先需要一个归纳假设,从栈底到栈顶,
现在设计势能函数以证明复杂度。一个
因此复杂度就是每个元素加入或撤销
struct queue2 {
stack<int, vector<int>> stk;
int cnt = 0;
void (*add)(int);
void (*undo)();
queue2() = default;
queue2(decltype(add) _add, decltype(undo) _undo) : add(_add), undo(_undo) {}
void push(int x) {
stk.push(x), add(x);
}
void pop() {
vector<int> tmp[2];
int lb = cnt & -cnt;
while (!stk.empty() && (int)tmp[0].size() < (lb ? lb : 1)) {
int x = stk.top();
stk.pop(), undo();
tmp[x > 0].push_back(x);
}
if (!cnt) {
swap(tmp[0], tmp[1]);
reverse(tmp[0].begin(), tmp[0].end());
for (int &x : tmp[0]) x = -x;
cnt = (int)tmp[0].size();
}
reverse(tmp[1].begin(), tmp[1].end());
for (int x : tmp[1]) add(x), stk.push(x);
reverse(tmp[0].begin(), tmp[0].end());
tmp[0].pop_back(), --cnt;
for (int x : tmp[0]) add(-x), stk.push(x);
}
};