洛谷 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\),也就是:
这时候可以把 \(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;
}