1 条题解
-
0
自动搬运
来自洛谷,原作者为

MrMorning
**搬运于
2025-08-24 21:47:43,当前版本为作者最后更新于2017-03-21 10:40:06,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
#Brief Description
小A有一个1-2^N的排列A[1..2^N],他希望将A数组从小到大排序,小A可以执行的操作有N种,每种操作最多可以执行一次,对于所有的i(1<=i<=N),第i中操作为将序列从左到右划分为2^{N-i+1}段,每段恰好包括2^{i-1}个数,然后整体交换其中两段.小A想知道可以将数组A从小到大排序的*不同*的操作序列有多少个,小A认为两个操作序列不同,当且仅当操作个数不同,或者至少一个操作不同(种类不同或者操作位置不同).
#Algorithm Design
首先不难发现操作顺序不影响答案, 我们只需要考察每种操作是否选中, 若选中交换哪两块就好了. 一个合法的操作序列如果有个操作, 那么可以给答案. 我们从小到大考察每一种操作, 首先, 可以知道, 对于操作, 序列肯定已经被分成了个有序数列, 我们首先检查是否有序, 如果有问题直接. 然后扫描每个块, 每两个块都必须是有序的, 否则要交换. 如果那么一定不合法. 代码表达的非常清楚.
#Code
#include <algorithm> #include <cstdio> #define ll long long const int maxn = 1 << 13; int n; int a[maxn]; ll po[13]; ll ans; bool check(int k) { for (int i = 1; i <= (1 << (n - k)); i++) if (a[(i - 1) * (1 << k) + 1] + (1 << (k - 1)) != a[(i - 1) * (1 << k) + (1 << (k - 1)) + 1]) return 0; return 1; } void swap(int i, int j, int k) { for (int m = 1; m <= k; m++) std::swap(a[i + m - 1], a[j + m - 1]); } void dfs(int now, int num) { if (now && !check(now)) return; if (now == n) { ans += po[num]; return; } dfs(now + 1, num); int tmp[5], tot = 0; for (int i = 1; i <= (1 << (n - now)); i += 2) if (a[i * (1 << now) + 1] != a[(i - 1) * (1 << now) + 1] + (1 << now)) { if (tot == 4) return; tmp[++tot] = i; tmp[++tot] = i + 1; } if (!tot) return; for (int i = 1; i <= tot; i++) for (int j = i + 1; j <= tot; j++) { swap((1 << now) * (tmp[i] - 1) + 1, (1 << now) * (tmp[j] - 1) + 1, 1 << now); dfs(now + 1, num + 1); swap((1 << now) * (tmp[i] - 1) + 1, (1 << now) * (tmp[j] - 1) + 1, 1 << now); } } int main() { // freopen("input", "r", stdin); po[0] = 1; for (int i = 1; i <= 12; i++) po[i] = po[i - 1] * i; scanf("%d", &n); for (int i = 1; i <= 1 << n; i++) scanf("%d", &a[i]); dfs(0, 0); printf("%lld", ans); }
- 1
信息
- ID
- 2395
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者