洛谷 P8866 [NOIP2022] 喵了个喵 题解
此题解纪念我第一次在 NOIP 考场上通过 T2。
首先考虑 的情况。这种情况只要用前 个栈放卡牌,最后一个栈用来消除下层卡牌。
定义“简单情况”表示所有栈中卡牌数量均不超过 2,且至少有一个空栈的情况。每种卡牌的第奇数、偶数张分别称为入栈牌、出栈牌。
再考虑 。如果现在已经有 个放了 2 张卡牌的栈和一个空栈,当前的牌为入栈牌,一种方法是放在空栈,另一种方法是放在一个已经有 2 张卡牌的栈顶。
但是,这两种方法都存在问题。方法一放完之后不存在空栈,如果接下来出现下层卡牌的出栈牌,那就寄了。方法二放完之后会存在一个有 3 张卡牌的栈,中间的牌消不掉。
注意到 3 张卡牌的栈如果在遇到中间牌的出栈牌之前就变成 2 张卡牌,就不会存在问题。所以,使用方法二时要找到一个上层牌出栈比下层牌晚的栈,下层牌必定先被消除,而其他栈里的牌都能在自己的栈和空栈中解决,不会有影响。消除之后就转化为简单情况。
如果找不到这样的栈怎么办?不存在上层牌出栈比下层牌晚的栈,则消除下层牌之前,对应的上层牌必定被消除。所以用方法一即可。
但是可能存在上层牌出栈之后立刻入栈的情况。对于这种情况,入栈时进行微调,选择有 1 张牌,且这张牌出栈较晚的栈放入即可。这样可以保证在消除第一张下层牌时,这张牌在栈顶,消除这张牌即可转化为简单情况。
最后利用空栈进行消除即可。
总结一下放牌的方法:遇到入栈牌,如果存在两个以上空栈,放入空栈即可;否则找有 1 张牌的栈,放入牌出栈最晚的栈;若没有,找有 2 张牌,且上层牌出栈比下层牌晚的栈;再找不到,就放入空栈。遇到出栈牌,如果消除的牌在栈顶,直接消去。如果消除的牌在栈底,此时必然存在空栈,利用空栈消除。
用 3 个 set 分别维护有 0 张、1 张、2 张牌的栈。
时间复杂度 。据说可以线性,但是我不会。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int N = 2e6 + 5;
int T, n, m, k;
int tim[N], last[N], a[N], pos[N];
int len[N], out[N][4];
set<pii, greater<pii>> q[3];
struct Triple {
int x, y, z;
Triple(int _x = 0, int _y = 0, int _z = 0) : x(_x), y(_y), z(_z) {}
};
vector<Triple> ans;
inline void put(int x, int s) {
pos[x] = s;
ans.emplace_back(1, s, 0);
}
void solve() {
for (int i = 1; i <= m; ++i) {
if (last[a[i]]) tim[last[a[i]]] = i, last[a[i]] = 0;
else last[a[i]] = i;
}
for (int i = 1; i <= n; ++i) {
q[0].insert(pii(0, i));
len[i] = 0;
}
for (int i = 1; i <= m; ++i) {
if (tim[i]) {
last[a[i]] = i;
if (q[0].size() >= 2u) {
auto it = q[0].begin();
int id = it->second;
q[0].erase(it);
out[id][++len[id]] = i;
put(i, id);
q[1].insert(pii(tim[i], id));
continue;
}
if (!q[1].empty()) {
auto it = q[1].begin();
int id = it->second, t = it->first;
q[1].erase(it);
out[id][++len[id]] = i;
put(i, id);
q[2].insert(pii(tim[i] > t, id));
continue;
}
if (!q[2].empty() && q[2].begin()->first) {
auto it = q[2].begin();
int id = it->second;
q[2].erase(it);
out[id][++len[id]] = i;
put(i, id);
continue;
}
auto it = q[0].begin();
int id = it->second;
q[0].erase(it);
out[id][++len[id]] = i;
put(i, id);
q[1].insert(pii(tim[i], id));
} else {
int j = last[a[i]], id = pos[j];
if (out[id][len[id]] == j) {
ans.emplace_back(1, id, 0);
if (len[id] == 1)
q[1].erase(pii(tim[out[id][1]], id));
else if (len[id] == 2)
q[2].erase(pii(tim[out[id][2]] > tim[out[id][1]], id));
--len[id];
if (len[id] == 0)
q[0].insert(pii(0, id));
else if (len[id] == 1)
q[1].insert(pii(tim[out[id][1]], id));
else
q[2].insert(pii(tim[out[id][2]] > tim[out[id][1]], id));
} else {
int tmp = q[0].begin()->second;
ans.emplace_back(1, tmp, 0);
ans.emplace_back(2, id, tmp);
if (len[id] == 1)
q[1].erase(pii(tim[out[id][1]], id));
else if (len[id] == 2)
q[2].erase(pii(tim[out[id][2]] > tim[out[id][1]], id));
--len[id];
for (int k = 1; k <= len[id]; ++k)
out[id][k] = out[id][k + 1];
if (len[id] == 0)
q[0].insert(pii(0, id));
else if (len[id] == 1)
q[1].insert(pii(tim[out[id][1]], id));
else
q[2].insert(pii(tim[out[id][2]] > tim[out[id][1]], id));
}
}
}
cout << ans.size() << '\n';
for (auto res : ans) {
if (res.x == 1)
cout << "1 " << res.y << '\n';
else
cout << "2 " << res.y << ' ' << res.z << '\n';
}
}
inline void clear() {
int mx = max({n, m, k});
for (int i = 1; i <= mx; ++i)
tim[i] = last[i] = a[i] = pos[i] = len[i] = 0;
q[0].clear();
q[1].clear();
q[2].clear();
ans.clear();
}
int main() {
freopen("meow.in", "r", stdin);
freopen("meow.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> T;
while (T--) {
cin >> n >> m >> k;
for (int i = 1; i <= m; ++i)
cin >> a[i];
solve();
clear();
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?