洛谷 P8866 [NOIP2022] 喵了个喵 题解

此题解纪念我第一次在 NOIP 考场上通过 T2。

首先考虑 k=2n2 的情况。这种情况只要用前 n1 个栈放卡牌,最后一个栈用来消除下层卡牌。

定义“简单情况”表示所有栈中卡牌数量均不超过 2,且至少有一个空栈的情况。每种卡牌的第奇数、偶数张分别称为入栈牌、出栈牌。

再考虑 k=2n1。如果现在已经有 n1 个放了 2 张卡牌的栈和一个空栈,当前的牌为入栈牌,一种方法是放在空栈,另一种方法是放在一个已经有 2 张卡牌的栈顶。

但是,这两种方法都存在问题。方法一放完之后不存在空栈,如果接下来出现下层卡牌的出栈牌,那就寄了。方法二放完之后会存在一个有 3 张卡牌的栈,中间的牌消不掉。

注意到 3 张卡牌的栈如果在遇到中间牌的出栈牌之前就变成 2 张卡牌,就不会存在问题。所以,使用方法二时要找到一个上层牌出栈比下层牌晚的栈,下层牌必定先被消除,而其他栈里的牌都能在自己的栈和空栈中解决,不会有影响。消除之后就转化为简单情况。

如果找不到这样的栈怎么办?不存在上层牌出栈比下层牌晚的栈,则消除下层牌之前,对应的上层牌必定被消除。所以用方法一即可。

但是可能存在上层牌出栈之后立刻入栈的情况。对于这种情况,入栈时进行微调,选择有 1 张牌,且这张牌出栈较晚的栈放入即可。这样可以保证在消除第一张下层牌时,这张牌在栈顶,消除这张牌即可转化为简单情况。

最后利用空栈进行消除即可。

总结一下放牌的方法:遇到入栈牌,如果存在两个以上空栈,放入空栈即可;否则找有 1 张牌的栈,放入牌出栈最晚的栈;若没有,找有 2 张牌,且上层牌出栈比下层牌晚的栈;再找不到,就放入空栈。遇到出栈牌,如果消除的牌在栈顶,直接消去。如果消除的牌在栈底,此时必然存在空栈,利用空栈消除。

用 3 个 set 分别维护有 0 张、1 张、2 张牌的栈。

时间复杂度 O(Slogn)。据说可以线性,但是我不会。

代码:

#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;
} 
posted @   猫猫NOIP2006  阅读(643)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
点击右上角即可分享
微信分享提示