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;
}