首页 > 其他分享 >[NOIP2022] 喵了个喵

[NOIP2022] 喵了个喵

时间:2023-11-07 20:23:45浏览次数:31  
标签:ch 空栈 int else push emp NOIP2022

补一下往年的构造题。。。

\(k\) 大概是 \(n\) 的两倍往下,这启示我们每个栈最多只放两个元素。

首先考虑 \(k=2n-2\) 的分,容易得到一个策略:留一个空栈不放,每个栈最多放两个。如果当前卡牌存在一个栈顶/栈底和它一样,那当前牌总是可以消掉的。否则当前栈中的卡牌一定两两不同,那一定还留有一个空位置没有放牌。

然后是 \(k=2n-1\),我们仍然用一样的策略,不同的是牌数恰好比位置多 \(1\),存在栈中放满了但是不能消掉的情况。设栈顶的牌种类集合为 \(S\),我们在牌堆中从上到下找到第一个不在 \(S\) 中的牌 \(u\),如果 \(u\) 是当前要放的牌,那就可以把当前放到空栈里,中间的牌一定能不用空栈消掉,放完之后再消掉空栈里的 \(u\) 即可。

否则 \(u\) 一定在某个栈的栈底出现过,我们记这个栈栈顶的元素为 \(v\),统计从当前要放的牌一直到 \(u\) 中 \(v\) 出现了多少次。如果出现了奇数次,那可以当前牌放空栈,剩下的牌该怎么放怎么放,放到 \(u\) 之后原本 \(u\) 这一列会变成新的空栈。否则将当前牌放到 \(u\) 为栈底这一列的顶端,然后剩下的牌该怎么放怎么放,一定能全消完,直到 \(u\) 的时候将 \(u\) 放进空栈,然后就可以消掉底部的 \(u\),此时空栈还是空栈,\(u\) 这一列也只会剩下两个元素。不断重复这个过程就可以构造出方案了。

#include <bits/stdc++.h>

using namespace std;

const int N = 2e6 + 5;

int T, n, m, k;

inline int read() {
	register int s = 0, f = 1; register char ch = getchar();
	while (!isdigit(ch)) f = (ch == '-' ? -1 : 1), ch = getchar();
	while (isdigit(ch)) s = (s * 10) + (ch & 15), ch = getchar();
	return s * f; 
}

int c[N];
deque<int> q[305];
set<int> emp;

vector<pair<int, pair<int, int >>> res;

set<int> S;
int tp[N], bt[N];

inline void remove(int x) {
	if (q[x].empty()) emp.erase(x);
	else { emp.insert(x); tp[q[x].back()] = 0; bt[q[x].front()] = 0; }
	if (q[x].size() < 2) S.erase(x);
	else S.insert(x);
}

inline void add(int x) {
	if (q[x].empty()) emp.insert(x);
	else { emp.erase(x); tp[q[x].back()] = x; bt[q[x].front()] = x; }
	if (q[x].size() < 2) S.insert(x);
	else S.erase(x);
}

inline void print() {
	cerr << "PRINT : " << endl;
	for (int i = 1; i <= n; ++i) {
		cerr << "STK " << i << " : ";
		deque<int> p = q[i];
		while (!p.empty()) cerr << p.front() << ' ', p.pop_front();
		cerr << endl; 
	} cerr << endl;
}

inline void push(int x) {
	res.push_back(make_pair(1, make_pair(x, 0))); int y = c[m--];
	remove(x); 
	if (q[x].empty() || q[x].back() != y) q[x].push_back(y);
	else q[x].pop_back();
	add(x);
}

inline void clear(int x, int y) {
	res.push_back(make_pair(2, make_pair(x, y)));
	if (q[x].empty() || q[y].empty() || q[x].front() != q[y].front()) { assert(false); }
	remove(x); remove(y); q[x].pop_front(); q[y].pop_front(); add(x); add(y);
}

int pt[N];

int main() {
	T = read();
	while (T--) {
		n = read(); m = read(); k = read();
		for (int i = 1; i <= m; ++i) c[i] = read();
		for (int i = 1; i <= n; ++i) emp.insert(i), S.insert(i);
		reverse(c + 1, c + m + 1);
		while (m) {
			int y = c[m];
			if (tp[y]) { push(tp[y]); continue; }
			if (bt[y]) { int x = *emp.begin(); int z = bt[y]; push(x); clear(x, z); continue; }
			bool flag = 0;
			for (int i : S) {
				if (i != *emp.begin()) {
					push(i);
					flag = 1; break;
				}
			} if (flag) continue; int e = *emp.begin();
			int l = m - 1; while (tp[c[l]]) pt[c[l]] = tp[c[l]], --l; int v = c[l];
			if (v == y) { push(e); while (m > l) push(pt[c[m]]); push(e); continue; }
			int w = q[bt[v]].back(); int p = bt[v]; int cnt = 0;
			for (int i = m - 1; i > l; --i) cnt += (c[i] == w);
			if (cnt & 1) {
				push(e);
				while (m > l) {
					if (c[m] == w) push(p);
					else push(pt[c[m]]);
				} push(p);
				continue;
			}
			push(p);
			while (m > l) {
				if (c[m] == w) push(p);
				else push(pt[c[m]]);
			} push(e); clear(e, p);
		} printf("%d\n", res.size());
		for (pair<int, pair<int, int> > i : res) {
			printf("%d ", i.first);
			if (i.first == 1) printf("%d\n", i.second.first);
			else printf("%d %d\n", i.second.first, i.second.second);
		} emp.clear(); S.clear();
		for (int i = 1; i <= n; ++i) assert(q[i].empty());
		for (int i = 0; i <= k; ++i) pt[i] = tp[i] = bt[i] = 0;
		res.clear();
	} return 0; 
} 

标签:ch,空栈,int,else,push,emp,NOIP2022
From: https://www.cnblogs.com/wwlwakioi/p/17815840.html

相关文章

  • P8868 [NOIP2022] 比赛
    传送门我们容易想到预处理区间\([l,r]\)中的\(m_a\timesm_b\)。这样算出来的是一个二维的矩阵,每次的答案就是红色部分:但是这样的问题是二维的,无论如何都不是正解。考虑把列这一维压掉,也就是令\(w'_i\leftarroww_{i,i}+w_{i,i+1}+...+w_{i,r}\)。这样询问的......
  • 题解:「NOIP2022 提高组」种花
    题解:「NOIP2022提高组」种花题目大意:给定一个\(n\timesm\)的01矩阵,0表示可以种花,1表示土坑(无法种花),现在要在图上种出一个C型或F型(C,F横着的两条线的长度都可以不同,但一定是面向右边的),现在问你种C和F分别有多少种方案(除了这个形状外不能在任何地方种花),多组数据,\(T\leq5\)。......
  • P8865 [NOIP2022] 种花 题解
    前言去年多测不清空导致即便CCF放过了我的\(O(n^2m)\)的代码但依然挂成了\(0pts\)。当时看清空数组后能过CCF数据就没再管。时隔\(1\)年,重做这道题写了\(O(nm)\)的正解,终于完成了当年的心愿。\(O(n^2m)\)思路想到计算方案的话可以维护两个数组\(c1_{i,j}\)表......
  • P8868 [NOIP2022] 比赛
    主要写一写标记的推导。理论大概在关于线段树上的一些进阶操作回忆一下普通历史和。是对两个合并队列做前缀和,然后利用往后插的贡献来计算。\(ht'+add*upd\toht\)\(s*upd+ht'*len\tohs\)下文:\(x\toadda,y\toaddb\)不带历史和的点积:\((a+x)(b+y)......
  • P8867 [NOIP2022] 建造军营
    面对他。题面:求选择关键点和不会被割的边,使得任意割去一条边关键点不会有不连通的方案。考虑缩边双,然后这样边双内随便选。你考虑画出一颗树,考虑分类情况,容易发现就是三种:1.没有选。2.全部连通上\(x\)。(即一个尚未孤立的连通块)。3.有不联通到\(x\)的点。(即孤立的一......
  • NOIP2022 比赛
    Day\(2^2+3^2+4^2\)。HNOI2016序列的加强版。我去年怎么这么菜啊,虽然现在也是就是了。\[\sum\limits_{[l,r]\in[L,R]}\left(\max\limits_{i\in[l,r]}a_i\right)\left(\max\limits_{i\in[l,r]}b_i\right)\]考虑离线,对右端点\(r\)扫描线,对每个左端点\(l\)维护\(S_l=\le......
  • P8867 [NOIP2022] 建造军营
    这道题想了很久,终于想出来了,非常抽象。经过一番无脑推导,我们发现u里面有没有军营,是否与根连通,u的子树有没有军营,……都对方案数有影响,然后我就一直修修改改,事实证明,当发现越来越多题目条件中被忽略的细节时,一定不要嫌麻烦,要从头开始设置状态。首先我们发现,子树中有没有军营对于......
  • P8868 [NOIP2022] 比赛
    https://www.luogu.com.cn/problem/P8868我学会了历史和!在一阵扫描线过后,你会发现,\([l,r]\)的所有子区间的答案,就一定是扫到\(i\)的时候,加上\([k,i]\)的答案,\(k\lei,i\in[l,r]\),然后又因为只有当\(i\gel\)的时候,才能对左端点在\([l,r]\)的答案贡献,因此,你会发现这个东......
  • 【题解】NOIP2022
    怎么看T3也不是那么难,可是为啥赛时就是被卡死了[难过]不补\(B\)题了,ad-hoc。A.种花题目描述:小C决定在他的花园里种出\(\texttt{CCF}\)字样的图案,因此他想知道\(\textttC\)和\(\textttF\)两个字母各自有多少种种花的方案;不幸的是,花园中有一些土坑,这些位置无法种......
  • CSP&NOIP2022游记
    今年是最后一年了,真的是来划水的了已经无欲无求了,只是最好能有个七级吧,要是没有也无所谓,反正我自始至终都是个OI废物已经完全回归whk咯谢幕之战,你会变好,还是更烂?冷知识:从去年CSP结束至今,Bosun在LG上只做了9题初赛前一天住了旅馆,周边玩了一下,感觉苏州古城区真的是一点意思也......