首页 > 其他分享 >NOIP2022 简要题解

NOIP2022 简要题解

时间:2023-02-18 06:44:05浏览次数:47  
标签:opt 简要 空栈 int 题解 卡牌 s2 NOIP2022 2n

前言

作为一名退役 OIer,借着此比赛来复健。

大部分题目都是口胡(没啥好写的),spj题手写了代码,A了。

难度不算特别高,但是赛场上拿到高分略有压力(退役了可以瞎bb)。

个人认为应该要拿到 300+ 的分数,但是感觉 AH 成绩普遍不好?

spj 题(尤其是构造题)一定要多重视啊,我当年没赶上这个好时代啊~

种花

洛谷标签为绿,pass

签到题,如果出事了建议去 pj 组历练。

懒得讨论。

喵了个喵

貌似是照应《羊了个羊》。是一道很有意思的 cf 类型题,大概会是 div1 中决定 rank 在 100 内还是外的分界线的难度。

此题在官方数据范围的指引下难度降了很多。首先 \(k=2n-2\) 十分容易,只需要维持一个空栈即可,其他每个栈至多两张卡牌。上层卡牌利用方法 1,下层卡牌利用方法 2 即可。

然后思考如何解决 \(k=2n-1\)。上面的策略在遇到连续 \(2n-1\) 个不同卡牌时,就不能维持一个空栈。首先在茫茫之中要明确一个原则:所有的栈中至多 \(2n-1\) 个卡牌,更进一步,不存在两个相同卡牌,否则将会把问题带向深渊,愈加复杂。

当遇到这种不可调和的情况时,仔细思考发现可以分 3 类情况讨论。首先先定义:栈中放入卡牌 \(X\) 后,所有栈有 \(2n-1\) 张不同卡牌,放 \(X\) 前有 \(2n-2\) 张卡牌,也就是说有 \(n-1\) 个栈,每个栈有两张卡牌。下层的卡牌集合记为 \(\{A\}\),上层为 \(\{B\}\)。

牌 \(X\) 之后,必然还会遇到一次 \(X\)。在 \(X\) 之前,我们可能会遇到 \(A\) 或 \(B\)。

第一种情况:在两次 \(X\) 之间全是 \(B\)。那么 \(X\) 放空栈即可,对后续操作不会有影响。

假如在两次 \(X\) 之间含有 \(A\),且第一个 \(A\) 在栈 \(i\) 中,栈 \(i\) 中还有一个上层卡牌 \(B_0\),第二种情况是:\(B_0\) 不在 \(X\) 到 \(A\) 之间,那么就把 \(X\) 放到第 \(i\) 个栈顶,然后后面正常操作,直到遇到 \(A\),此时空栈仍然是空栈,利用其消去 \(A\),那么仍然存在空栈并且其它栈元素均不超过 2,可以继续。

那么当 \(B_0\) 出现在 \(X\) 和 \(A\) 之间,采用上面的方法会导致栈中已有的 \(B_0\) 无法消去,于是为第三种情况。那么就把 \(X\) 放到空栈里,当操作到 \(B_0\) 时,\(i\) 栈仅剩一个元素 \(A\),如果之后再次遇到 \(B_0\),就放到 \(X\) 顶上,这样遇到了 \(A\) 以后,直接放在 \(i\) 栈即可消去 \(A\),此时 \(i\) 栈变成了空栈,并且其它栈元素均不超过 2。

复杂度 \(\mathcal O(n+m)\)。

#include <bits/stdc++.h>
const int N = 305, M = 2e6 + 5;
int T, n, m, k, a[N], b[N], c[N * 2], cur, s[M];
std::stack<int> s1, s2;
struct step { int x, y, z; };
std::vector<step> ans;
void opt_1(int x) {
	ans.push_back({1, x, 0});
}
void opt_2(int x, int y) {
	ans.push_back({2, x, y});
}
bool solve(int x) {
	if (c[x]) {
		if (c[x] < 0) {
			int u = -c[x];
			opt_1(u); b[u] = 0;
			s2.push(u);
		}
		else {
			int u = c[x];
			opt_1(cur); opt_2(u, cur);
			a[u] = b[u];
			if (b[u]) {
				c[b[u]] *= -1;
				b[u] = 0;
				s2.push(u);
			}
			else s1.push(u);
		}
		c[x] = 0;
	}
	else if (!s1.empty()) {
		int u = s1.top(); s1.pop();
		opt_1(u);
		a[u] = x; c[x] = u; // 第一层 +
	}
	else if (!s2.empty()) {
		int u = s2.top(); s2.pop();
		opt_1(u);
		b[u] = x; c[x] = -u; // 第二层 -
	}
	else return false;
	return true;
}
int main() {
	scanf("%d", &T);
	while (T--) {
		scanf("%d%d%d", &n, &m, &k);
		for (int i = 1; i <= m; i++)
			scanf("%d", &s[i]);
		while (!s1.empty()) s1.pop();
		while (!s2.empty()) s2.pop();
		ans.clear();
		cur = n;
		for (int i = 1; i < n; i++)
			s1.push(i), s2.push(i);
		for (int i = 1; i <= m; i++)
			if (!solve(s[i])) {
				int j = i + 1;
				while (s[j] != s[i] && c[s[j]] < 0) j++;
				if (s[j] == s[i]) {
					opt_1(cur);
					for (int k = i + 1; k < j; k++)
						solve(s[k]);
					opt_1(cur);
				}
				else {
					int u = c[s[j]], p = 0;
					for (int k = i + 1; k < j; k++)
						if (c[s[k]] == -u) { p = k; break; }
					if (p) {
						opt_1(cur);
						c[a[cur] = s[i]] = cur;
						for (int k = i + 1; k < p; k++)
							solve(s[k]);
						opt_1(u);
						b[u] = c[s[p]] = 0; s2.push(cur);
						for (int k = p + 1; k < j; k++)
							solve(s[k]);
						opt_1(u);
						a[u] = c[s[j]] = 0;
						cur = u;
					}
					else {
						opt_1(u);
						for (int k = i + 1; k < j; k++)
							solve(s[k]);
						opt_1(cur); opt_2(u, cur);
						c[s[j]] = 0;
						c[a[u] = b[u]] = u;
						c[b[u] = s[i]] = -u;
					}
				}
				i = j;
			}
		printf("%lu\n", ans.size());
		for (step o : ans)
			if (o.x == 1) printf("1 %d\n", o.y);
			else printf("2 %d %d\n", o.y, o.z);
	}
	return 0;
}

建造军营

此题很裸,直接 缩点 + DP 计个数即可。没什么好说的。

比赛

看了眼数据范围 + 时限,直接分块。细节没想好,想起来了就补。

好像可以 线段树?不管了我 DS 很菜,想起来再补。

标签:opt,简要,空栈,int,题解,卡牌,s2,NOIP2022,2n
From: https://www.cnblogs.com/ac-evil/p/17131910.html

相关文章

  • 题解 CF690C2
    题目大意:给你一棵树,求一下直径题目分析:emm,怎么说吧,就是树的直径的裸板子。可能有人不大理解,明明是图,你为什么要说是给定一棵树。大家可以自行验证一下,满足如下两个性......
  • 题解 CF690C1
    题目大意:给定一张\(n\)个点\(m\)条边的无向图,判断这是不是一棵树。题目分析:两种思路:思路一:不需要建图,直接使用并查集判环即可最后判断一下图联不联通就行,具体方......
  • 一个autoreconf的报错问题解决
    报错如下configure.ac:36:error:possiblyundefinedmacro:AC_CHECK_LIBIfthistokenandothersarelegitimate,pleaseusem4_pattern_allow.Seet......
  • 题解 CF637B
    题目大意:维护个栈,去重保留最上层题目分析:啥也不是,数组模拟\(\text{stack}+\text{unordered\_map}\)直接秒掉。复杂度\(O(n)\)代码实现:#include<bits/stdc++.h>......
  • 题解 CF742B
    题目大意:给定\(n\)个数,找数对使其异或值为\(k\),求满足这样数对的个数。题目分析:考验位运算功底的题目(实际上也不是很难),主要运用到了下列性质:\[\begin{aligned}\bec......
  • 20230207模拟赛题解
    A-CF755D考虑每次加边产生的贡献。发现每次加边的贡献是这条边与别的边的交点数量加\(1\)。所以可以用线段树或树状数组等数据结构维护,注意要令\(k=\min(k,n-k)\)。B-......
  • CAD坐标显示不全怎么办?CAD坐标常见问题解答!
    今天小编来和大家聊一下浩辰CAD看图王中关于CAD坐标的那些事,比如:CAD坐标为何显示不全?CAD坐标显示结果和之前不一样?以及不能精准捕捉CAD坐标等情况,应该如何轻松解决?今天就和......
  • 0210 模拟题解
    0210模拟题解t1直接枚举\(k\),考虑计算答案。首先发现这个限制等价于存在长度\(\gen-k\)的上升子段。那我们反过来,计算所有上升子段长度都\(\len-k-1\)的方案数......
  • CF、AT 杂题题解
    CF1455Fsolution1前\(i\)次操作只会影响到\([1,i+1]\),并且在第\(i\)次操作前,原本在位置\(i\)的数只可能在\(i\)或\(i-1\)。于是就可以考虑设\(f_{i,0/1}\)......
  • YACS 2023年1月月赛 乙组 T4 加与乘(二) 题解
    题目链接应大家的要求,早上起来更一下乙组T4。这一道题目我们发现不仅会加元素了,还会重复执行任务。很容易想到用两个树状数组来维护每个任务的执行次数,以及每个单元格......