2021“MINIEYE杯”中国大学生算法设计超级联赛(9)
Boring data structure problem
本题名字叫数据结构,但是其实并不需要很复杂的东西去维护,因为题目要求输出队列第\(\lceil\frac{m+1}{2}\rceil\)个数即中间那个数,所以我们可以借鉴对顶堆的思想:
\(\bullet\) 分别开两个双端队列\(q_1\),\(q_2\),其中\(q_1\)用于存储操作一的数,\(q_2\)用于存储操作二的数,如果是操作一,在\(q_1\)队首插入,如果是操作二,在\(q_2\)队尾插入。
\(\bullet\) 保证\(q_1\)有效数字个数始终 \(\geq\) \(q_2\)有效数字个数,什么是有效数字呢,假设\(q_1\)为\([1,3,7]\),现在要删除\(3\),那么它的有效数字就从\(3\)个变成了\(2\)个,但是我们不是立马弹出,而是等两个队列间转移时顺便删除,有点像线段树\(lazy\)标记的思想,所以我们进行两个队列转移时的判断条件不是两个队列的大小了,而是需要用两个变量\(size_1\),\(size_2\)去维护。
\(\bullet\) 对于删除操作,我们用一个数组\(belong\)记录它是在哪个队列里的,\(0\)代表在\(q_1\),\(1\)代表在\(q_2\),然后将这个数所在的队列有效数字个数\(-1\),然后在转移时顺便删除即可,即只弹出不再入队,同时,我们还需要在转移中更新那些没有删除的元素的\(belong\)。
\(\bullet\) 对于查询操作,我进行了分类讨论,用一个数\(cnt\)记录两个队列有效数字之和,如果总个数为奇数,那么输出\(q_1\)队尾元素,否则输出\(q_2\)队首元素。还有一点需要注意,我们只是在队列转移时顺便弹出了要删除的数字,有可能存在不需要转移并且当前输出元素是被删除的数字的情况我们我们,只需再弹出即可。
关于队列元素的更新方式可以将代码中注释的那一行取消注释,其中分别打印了\(q_1\)和\(q_2\)。
#include <bits/stdc++.h>
using namespace std;
template<typename T>
ostream &operator <<(ostream &os, deque<T> Q) {
os << '[';
while (!Q.empty()) {
os << Q.front();
Q.pop_front();
if (!Q.empty()) {
os << ", ";
}
}
os << ']';
return os;
}
const int MAXN = 1e7 + 5;
bool vis[MAXN];
int belong[MAXN];
int main(int argc, char *argv[]) {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
deque<int> q1, q2;
int q;
cin >> q;
int num = 0, cnt = 0, sz1 = 0, sz2 = 0;
// num代表下个元素编号, cnt为两个队列有效元素个数和, sz1, sz2分别记录两个队列有效元素个数
while (q--) {
char c;
cin >> c;
if (c == 'L') {
++cnt, ++sz1;
q1.push_front(++num);
belong[num] = 0;
}
if (c == 'R') {
++cnt, ++sz2;
q2.push_back(++num);
belong[num] = 1;
}
if (c == 'Q') {
while (!q2.empty() && sz1 < sz2 + 1) {
// 要删除的数字只需弹出不需再次入队
if (vis[q2.front()]) {
q2.pop_front();
continue;
}
// 进行一系列维护操作
belong[q2.front()] = 0;
q1.push_back(q2.front());
q2.pop_front();
++sz1, --sz2;
}
while (!q1.empty() && sz1 > sz2 + 1) {
if (vis[q1.back()]) {
q1.pop_back();
continue;
}
belong[q1.back()] = 1;
q2.push_front(q1.back());
q1.pop_back();
++sz2, --sz1;
}
// 这两个while循环就是不断弹出不满足条件的情况
while (!q1.empty() && vis[q1.back()]) q1.pop_back();
while (!q2.empty() && vis[q2.front()]) q2.pop_front();
// cout << q1 << ' ' << q2 << '\n';
cout << (cnt & 1 ? q1.back() : q2.front()) << '\n';
}
if (c == 'G') {
--cnt; // 总有效个数-1
int x;
cin >> x;
vis[x] = true;
--(belong[x] & 1 ? sz2 : sz1);
// 将这个数对应的队列有效数字个数-1
}
}
system("pause");
return 0;
}