P10360 [PA2024] Desant 3 - Solution

· · 题解

我们先令 $a_x = 1$ 代表 $x$ 准备好了,$a_x = 0$ 代表 $x$ 没有准备好。 于是考虑暴力。你考虑最基础的暴力,就是直接枚举 $2^{n}$ 种状态直接判定,复杂度 $\Theta((n + m)2^n)$,肯定炸。 这个操作仍然太过于抽象,如果是正常的求,几乎想不到任何除了暴力外的做法。 但是题目为什么要对 $2$ 取模呢?对 $2$ 取模就是一个突破口,实际上,这类对 $2$ 取模的计数题,经常是寻找方案之间的对偶性。如果我们知道 $x$ 的后继有两种可能,而且递归计算这两种后继的贡献的奇偶性一定相等(我们并不关心它们具体贡献了什么,而只关心它们的奇偶性到底相等不相等),那么我们就可以直接忽略掉这两个后继,不计算它们。毕竟它们最后对答案(对 $2$ 取模)不存在任何影响。 这确实是一种套路,没见过的人感觉是挺难想到的,我自己还是在信友队的 NOIP 模拟赛上学到这个技巧的,比如 [AGC050F](https://www.luogu.com.cn/problem/AT_agc050_f) 就是这种类型的好题。 于是考虑二进制枚举应该没什么出路了,我们考虑搜索然后根据这个性质能不能剪枝。 刚开始,我们想象整个序列都是问号,然后我们要在问号里填数。 不妨枚举每个操作,保存我们当前存在的状态。 比如现在操作要我们交换 $x,\,y$。 - $x,\,y$ 都是问号,这其实比较棘手。分开讨论,如果 $x,\,y$ 状态不同,比如我们令 $a_x = 1,\,a_y = 0$ 或者 $a_x = 0,\,a_y = 1$。发现了吗?$a_x = 1,\,a_y = 0$ 经过操作一定会变换成 $a_x = 0,\,a_y = 1$。于是我们就有了两个后继状态,它们是一模一样的,对答案显然没影响(对 $2$ 取模),所以我们可以直接忽略掉它们状态不同的可能。于是只需要钦定 $a_x,\,a_y$ 状态相同即可,枚举 $0/1$ 状态暴搜即可。 - $x,\,y$ 中只有一个问号,如果问号在 $y$ 上且 $a_x = 0$ 就忽略掉,否则 $a_x = 1$。此时考虑 $a_y$ 的取值,要么为 $0$ 要么为 $1$。如果是 $0$ 那么就交换,否则不交换。看起来产生了两个新状态,但其实是一个。我们的后继状态一定 $a_x' = a_y,\,a_y' = 1$,于是只产生了唯一的后继状态,也就是 $x$ 位置是问号且 $a_y = 1$。问号在 $x$ 上是对称的,也只有一个后继状态。 - $x,\,y$ 中没有问号,那直接根据题意交换或不交换即可。 只有第一种情况会产生多个(两个)后继。这里每次决策会恰好去掉两个问号,问号总数是 $\Theta(n)$ 的,决策只有 $\lceil\frac{n}{2}\rceil$ 级别。也就是状态数是 $2^{\lceil\frac{n}{2}\rceil}$ 级别的。 哦还有一个小细节,就是我们经过 $m$ 个操作之后可能有状态仍然包含问号。 这如何统计答案?发现这些问号我们直接钦定区间里面是 $1$ 区间外面都是 $0$ 即可,对答案没影响。 时间复杂度 $\Theta((n^2 + m)\sqrt{2^n})$,最终状态数卡满大概 $2^{17}$ 级别,可以通过。 ```cpp #include <bits/stdc++.h> #define X first #define Y second #define rep(i, a, b) for (int i = a; i <= b; i++) #define per(i, a, b) for (int i = a; i >= b; i--) #define pb push_back #define mp make_pair using namespace std; typedef long long int ll; using ull = unsigned long long int; using pii = pair<int, int>; using pli = pair<ll, int>; using pq = priority_queue<int>; using ld = double; constexpr int maxn = 1e5 + 10, bs = 19260817, mod = 22309287; constexpr ll inf = 1.1e14; int a[42], n, m, l[maxn], r[maxn], ans[42]; void dfs(int u) { if (u == m + 1) { int cnt = 0; rep(i, 1, n) cnt += (a[i] == 1); for (int i = 1, v = 0; i <= n; i++, v = 0) rep(j, i, n) { if (a[j] == 0) break; if (((v += (a[j] == 1)) == cnt)) ans[j - i + 1] ^= 1; } return; } int x = l[u], y = r[u]; if (a[x] == -1 && a[y] == -1) a[x] = a[y] = 0, dfs(u + 1), a[x] = a[y] = 1, dfs(u + 1), a[x] = a[y] = -1; else if (a[x] == -1 || a[y] == -1) { if (a[x] == 0 || a[y] == 1) dfs(u + 1); else if (a[x] == 1) { a[x] = -1, a[y] = 1; dfs(u + 1); a[x] = 1, a[y] = -1; } else if (a[y] == 0) { a[x] = 0, a[y] = -1; dfs(u + 1); a[x] = -1, a[y] = 0; } } else { if (a[x] == 1 && a[y] == 0) a[x] = 0, a[y] = 1, dfs(u + 1), a[x] = 1, a[y] = 0; else dfs(u + 1); } } int main() { scanf("%d%d", &n, &m); rep(i, 1, m) scanf("%d%d", &l[i], &r[i]); memset(a, -1, sizeof(a)); dfs(1); rep(i, 1, n) printf("%d ", ans[i]); return 0; } ```