爆炸后的颓大抵是到此为止了。
(k = 2n - 2):
单独地拎出一个空栈作为特殊栈,其余的 (n - 1) 个普通栈维护 (2n - 2) 张不同花色的牌,并保证每个栈的高度均不超过 (2)。
此时新进一张牌:
- 若位于某个栈的栈顶:直接将其入栈,自动消除。
- 若位于某个栈的栈底:将其放入特殊栈,随后执行 2 操作。
- 否则,将其放入任意一个高度小于 (2) 的普通栈中。
(k = 2n - 1):
还是用 (n - 1) 个栈维护 (2n - 2) 张不同花色的牌。
若此时新进一张牌 (w),且不满足上述的三种情况,即:(w) 不在任何一个栈中,也没有任何一个普通栈高度小于 (2)。
再次考虑,对于这种情况,我们所进行的操作的目的到底是什么?
找到一种策略,使得在放入 (w) 后,可以在保证不出现无解情况的基础上通过一系列的操作再整理出一个空栈作为特殊栈,同时保证所有栈的高度都不超过 (2)。
怎样保证不出现无解情况呢?
题目保证数据本身必定有解,那么我们只需确保任意时刻都没有两张无法互相消除的花色相同的牌位于栈中即可,也就是说,每新进来一张牌,若它在栈中,则一定会消除。
关注到我们只有在需要消除栈底元素时才会用到特殊栈,很容易想到第一种情况:当 (w) 在任何一个栈底元素出现前再次出现时,直接将两次出现的 (w) 都放入特殊栈中消掉即可。
否则,则会有至少一个栈底元素先于 (w) 出现。此时,我们只能在最先出现的栈底元素上做文章。理由也很简单:该元素出现后就需要特殊栈发挥作用了,必须在此之前整理出一个空栈作为特殊栈。
设这个最先出现的栈底元素为 (x),该栈为 (id),其栈顶元素为 (y)。
- 在 (x) 再次出现前,若 (y) 出现了奇数次。
将 (w) 放入原先的特殊栈中,然后将所有的 (y) 和最后的 (x) 放入 (id)(因为奇数个 (y) 可以在 (x) 再次出现前将 (x) 顶上的一个 (y) 消去,此时直接放入 (x) 就能清空 (id)),最后将 (id) 作为新的特殊栈即可。 - 在 (x) 再次出现前,若 (y) 出现了偶数次。
将 (w) 放入 (id) 中,并将所有的 (y) 和 最后的 (x) 都放入特殊栈即可(因为有偶数个 (y),所以 (x) 再次出现时特殊栈一定为空,此时直接将 (x) 放入特殊栈并对 (id) 和特殊栈执行 2 操作,就能使得所有栈的高度不超过 (2),同时存在一个空栈)。注意这种情况下不需要变更特殊栈。
时间复杂度 (O(sum nm))。
代码:
#include <bits/stdc++.h>
#define MAXN 310
#define MAXM 2000100
using namespace std;
int T, n, m, p, a[MAXM], b[MAXN << 1];
int spec;
bool vis[MAXN << 1];
struct Ans {
int op, x, y;
};
deque<int> stk[MAXN];
vector<Ans> ans;
template<typename _T>
void read(_T &_x) {
_x = 0;
_T _f = 1;
char _ch = getchar();
while (_ch < '0' || '9' < _ch) {
if (_ch == '-') _f = -1;
_ch = getchar();
}
while ('0' <= _ch && _ch <= '9') {
_x = (_x << 3) + (_x << 1) + (_ch & 15);
_ch = getchar();
}
_x *= _f;
}
template<typename _T>
void write(_T _x) {
if (_x < 0) {
putchar('-');
_x = -_x;
}
if (_x > 9) write(_x / 10);
putchar('0' + _x % 10);
}
bool putin(int c) {
for (int i = 1; i <= n; i++) {
if (i == spec) continue;
if (stk[i].size() < 2) {
ans.emplace_back(Ans{1, i});
stk[i].push_back(c);
vis[c] = 1;
return 1;
}
}
return 0;
}
void del(int c) {
for (int i = 1; i <= n; i++) {
if (stk[i].empty()) continue;
if (c == stk[i].back()) {
ans.emplace_back(Ans{1, i});
stk[i].pop_back();
vis[c] = 0;
break;
} else if (c == stk[i].front()) {
ans.emplace_back(Ans{1, spec});
ans.emplace_back(Ans{2, i, spec});
stk[i].pop_front();
vis[c] = 0;
break;
}
}
}
int find(int c) {
for (int i = 1; i <= n; i++) {
if (i == spec) continue;
if (c == stk[i].front()) return i;
}
return 0;
}
void solve1(int l, int r) {
ans.emplace_back(Ans{1, spec});
for (int i = l + 1; i < r; i++) {
if (!vis[a[i]]) putin(a[i]);
else del(a[i]);
}
ans.emplace_back(Ans{1, spec});
}
void solve2(int l, int r, int id, int c) {
ans.emplace_back(Ans{1, spec});
stk[spec].push_back(a[l]);
vis[a[l]] = 1;
spec = id;
stk[spec].pop_back();
vis[c] = 0;
for (int i = l + 1; i <= r; i++) {
if (a[i] == c) {
ans.emplace_back(Ans{1, spec});
continue;
}
if (!vis[a[i]]) putin(a[i]);
else del(a[i]);
}
}
void solve3(int l, int r, int id, int c) {
ans.emplace_back(Ans{1, id});
stk[id].push_back(a[l]);
vis[a[l]] = 1;
for (int i = l + 1; i <= r; i++) {
if (a[i] == c) {
ans.emplace_back(Ans{1, spec});
continue;
}
if (!vis[a[i]]) putin(a[i]);
else del(a[i]);
}
}
int main() {
read(T);
while (T--) {
ans.clear();
for (int i = 1; i <= p; i++) vis[i] = 0;
for (int i = 1; i <= n; i++) stk[i].clear();
read(n), read(m), read(p);
for (int i = 1; i <= m; i++) read(a[i]);
spec = n;
int cur = 0;
while (cur < m) {
cur++;
if (!vis[a[cur]]) {
if (!putin(a[cur])) {
for (int i = 1; i <= p; i++) b[i] = 0;
int k = cur;
while (k < m) {
k++;
if (a[k] == a[cur]) {
solve1(cur, k);
break;
}
b[a[k]]++;
int bel = find(a[k]);
if (bel) {
if (b[stk[bel].back()] & 1) solve2(cur, k, bel, stk[bel].back());
else solve3(cur, k, bel, stk[bel].back());
break;
}
}
cur = k;
}
} else del(a[cur]);
}
write(ans.size()), putchar('n');
for (int i = 0; i < ans.size(); i++) {
if (ans[i].op == 1) putchar('1'), putchar(' '), write(ans[i].x);
else putchar('2'), putchar(' '), write(ans[i].x), putchar(' '), write(ans[i].y);
putchar('n');
}
}
return 0;
}