洛谷 P9591 「PFLOI R1」PFL 变换

洛谷传送门

NOIP 模拟赛 T1。

避免被 corner case 卡,首先暴力特判 \(n \le 7\),以及 \(m = 1\)

\(t = 2^{\left\lfloor\log_2 n\right\rfloor + 1} - 1\)

手玩一下 \(n = 20\)。转化一下我们要在 \(1 \sim 20\) 中去除 \(n - m\) 个数,使得剩下的数异或和为 \(t\)

因为 \(1 \oplus 2 \oplus 3 \oplus \cdots \oplus 20 = 20\)\(t = 31\),所以 \(m = 20\) 显然无解。

考虑 \(m = 19\),发现我们可以把 \(11\) 去掉,变成 \(1 \sim 10, 12 \sim 20\)

\(m = 18\) 时,发现 \(10 \oplus 1 = 11\),所以可以变成 \(2 \sim 9, 11 \sim 20\)

\(m = 17\) 时,我们没有 \(1\) 了,需要弄出一个 \(1\)。发现可以合并 \(2, 3\),变成 \(1, 4 \sim 9, 11 \sim 20\)

发现这个过程是可以一直做的。如果有 \(1\) 就把 \(10\) 变成 \(11\) 或者把 \(11\) 或者 \(10\),没有就选两个最小的相邻的数,使得它们的异或是 \(1\),合并它们。

这个做法是可以推广到所有 \(n\) 为偶数的,因为 \(n\) 为偶数时 \(1 \oplus 2 \oplus \cdots \oplus n = n\)\(n + 1\)

但是发现有例外。当 \(n = 2^k - 2\) 时,\(1 \oplus 2 \oplus \cdots \oplus n = t\),例如 \(n = 14\) 时,\(1 \oplus 2 \oplus \cdots \oplus 14 = 15\),意味着 \(m = 14\) 有解,且 \(m = 12, 13\) 都无解。\(m = 11\) 时可以把 \(1, 2, 3\) 抠出来,变成 \(4, 5, \ldots, 14\)\(m = 10\) 时,合并 \(4, 6\),得 \(2, 5, 7, 8, \ldots, 14\)。之后采取这样的策略,如果有 \(1\) 就把 \(1\) 和当前除 \(1\) 的最小数合并,否则制造一个 \(1\),也就是:

\[1, 2, 5, 10, 11, 12, 13, 14 \]

\[3, 5, 10, 11, 12, 13, 14 \]

\[1, 3, 5, 12, 13, 14 \]

\[2, 5, 12, 13, 14 \]

\[1, 2, 5, 14 \]

\[3, 5, 14 \]

这时候可以把 \(3, 5\) 合并变成 \(6, 14\)

\(n\) 为奇数时,\(1 \oplus 2 \oplus \cdots \oplus n = 0\)\(1\)。此时 \(m = n - 1\) 无解。对于 \(m = n - 2\),例如 \(n = 19, m = 17\) 时,可以把 \(8, 6\) 抠出来变成 \(1 \sim 5, 7, 9 \sim 19\)。发现可以按 \(n\) 为偶数做了,有 \(1\) 就把 \(1\)\(7\) 合并得到 \(6\)\(1\)\(6\) 合并得到 \(7\),否则合并最小的相邻两个数使得它们异或起来是 \(1\)

code
vector<int> haruka_sora(int n, int m) {
	int s = 0, t = (1 << (__lg(n) + 1)) - 1;
	if (m == 1) {
		return n == t ? vector<int>(1, n) : vector<int>();
	}
	if (n <= 7) {
		for (int S = 0; S < (1 << n); ++S) {
			if (__builtin_popcount(S) != m) {
				continue;
			}
			int x = 0;
			for (int i = 1; i <= n; ++i) {
				if (S & (1 << (i - 1))) {
					x ^= i;
				}
			}
			if (x == t) {
				vector<int> ans;
				for (int i = 1; i <= n; ++i) {
					if (S & (1 << (i - 1))) {
						ans.pb(i);
					}
				}
				return ans;
			}
		}
		return vector<int>();
	}
	for (int i = 1; i <= n; ++i) {
		s ^= i;
	}
	if (m == n) {
		if (s == t) {
			vector<int> ans;
			ans.resize(n);
			for (int i = 0; i < n; ++i) {
				ans[i] = i + 1;
			}
			return ans;
		} else {
			return vector<int>();
		}
	}
	if (s == t) {
		if (m == n - 1 || m == n - 2) {
			return vector<int>();
		}
		if (m == n - 3) {
			vector<int> ans;
			ans.resize(n - 3);
			for (int i = 4; i <= n; ++i) {
				ans[i - 4] = i;
			}
			return ans;
		}
		vector<int> ans;
		ans.resize(n - 4);
		int len = 0;
		for (int i = n; i >= 5; --i) {
			if (i != 6) {
				ans[len++] = i;
			}
		}
		ans[len++] = 2;
		for (int _ = n - 4; _ > m; --_) {
			if (ans.back() == 1) {
				int x = ans.back();
				ans.pop_back();
				x ^= ans.back();
				ans.pop_back();
				ans.pb(x);
			} else {
				bool flag = 1;
				for (int i = (int)ans.size() - 1; i; --i) {
					if ((ans[i] ^ ans[i - 1]) == 1) {
						ans.erase(ans.begin() + i);
						ans.erase(ans.begin() + i - 1);
						ans.pb(1);
						flag = 0;
						break;
					}
				}
				if (flag) {
					break;
				}
			}
		}
		if ((int)ans.size() == m) {
			return ans;
		}
		reverse(ans.begin(), ans.end());
		while ((int)ans.size() > m) {
			int x = ans[0], y = ans[1];
			ans.erase(ans.begin());
			ans.erase(ans.begin());
			ans.insert(ans.begin(), x ^ y);
		}
		return ans;
	}
	if ((s ^ t) <= n) {
		vector<int> ans;
		ans.resize(n - 2);
		int len = 0;
		for (int i = n; i; --i) {
			if (i != (s ^ t) && i != (s ^ t ^ 1)) {
				ans[len++] = i;
			}
		}
		int x = s ^ t ^ 1;
		for (int _ = n - 1; _ > m; --_) {
			if (ans.back() == 1) {
				x ^= 1;
				ans.pop_back();
			} else {
				for (int i = (int)ans.size() - 1; i; --i) {
					if ((ans[i] ^ ans[i - 1]) == 1) {
						ans.erase(ans.begin() + i);
						ans.erase(ans.begin() + i - 1);
						ans.pb(1);
						break;
					}
				}
			}
		}
		ans.pb(x);
		return ans;
	}
	if (m == n - 1) {
		return vector<int>();
	}
	int x = (1 << __lg(n));
	int y = s ^ t ^ x ^ 1;
	vector<int> ans;
	ans.resize(n - 3);
	int len = 0;
	for (int i = n; i; --i) {
		if (i != x && i != y && i != (y ^ 1)) {
			ans[len++] = i;
		}
	}
	for (int _ = n - 2; _ > m; --_) {
		if (ans.back() == 1) {
			y ^= 1;
			ans.pop_back();
		} else {
			for (int i = (int)ans.size() - 1; i; --i) {
				if ((ans[i] ^ ans[i - 1]) == 1) {
					ans.erase(ans.begin() + i);
					ans.erase(ans.begin() + i - 1);
					ans.pb(1);
					break;
				}
			}
		}
	}
	ans.pb(y);
	return ans;
}
posted @ 2023-11-09 14:48  zltzlt  阅读(49)  评论(0编辑  收藏  举报