这个题哎呀。。。细节超级多。。。
首先,我猜了一个结论。如果有一种排序方案是可行的,假设这个方案是 $S$ 。
那么我们把 $S$ 给任意重新排列之后,也必然可以构造出一组合法方案来。
于是我们就可以 $O(2^n)$ 枚举每个操作进不进行,再去判断,如果可行就 $ans$ += $|S|!$。
然而怎么判断呢?
我们按照操作种类从小到大操作。
假设我们现在在决策第 $i$ 种操作并且保证之前之后不需要进行种类编号 $< i$ 的操作。
那么我们只考虑那些位置在 $2^i+1$ 的位置的那些数。
我们先找到所有的非法位置,满足下列条件之一就是非法位置,设当前位置为 $pos$:
- 条件$1$:$A_{pos}$ 不能表示成 $a imes 2^i + 1$,其中 $a$ 为非负整数。
- 条件$2$:$A_{pos + 2^{i-1}} e A_{pos} + 2^{i-1}$。
满足以下三种情况之一即为无解:
- 如果有三个或以上个非法位置,那么显然就无解了。
- 如果没有非法位置,但是当前状态下必须要进行一次操作,那么无解。
- 如果有非法位置,但是当前状态下必须不进行操作,那么无解。
那么现在开始讨论:
当没有非法位置的时候,直接递归求解。
当只有一个非法位置的时候,交换 $A_{pos}$ 和 $A_{pos + 2^{i-1}}$,递归求解。
这里本来应该交换一个区间的,然而有用的其实只有 $A_{pos}$ 和 $A_{pos + 2^{i-1}}$ 这两个数。
因为其他的数字我们现在不会考虑,以后也更不用考虑了,反正保证是合法的,所以我们只交换这两个数。
当有两个非法位置的时候,设两个非法位置为 $pos_1$ 和 $pos_2$
我们把非法位置分两类:第一类是满足条件 $1$ 的,第二类是不满足条件 $1$ 但满足条件 $2$ 的。
- $type(pos_1) = 2$ && $type(pos_2) = 2$:可以有两种可能合法的交换:交换 $A_{pos_1}$ 和 $A_{pos_2}$ 或者交换 $A_{pos_1 + 2^{i - 1}}$ 和 $A_{pos_2 + 2^{i - 1}}$。
- $type(pos_1) = 2$ && $type(pos_2) = 1$:只有一种可能合法的交换:交换 $A_{pos_1 + 2^{i - 1}}$ 和 $A_{pos_2}$。
- $type(pos_1) = 1$ && $type(pos_2) = 2$:只有一种可能合法的交换:交换 $A_{pos_1}$ 和 $A_{pos_2 + 2^{i - 1}}$。
- $type(pos_1) = 1$ && $type(pos_2) = 1$:无解。
然后递归求解即可。注意每当交换完之后要判断原来的非法位置是否变合法了,如果还是非法的那么就直接判掉。
判断每个状态的复杂度:
$$sum_{i=1}^{n}2^i imesfrac{2^n}{2^i} = sum_{i=1}^{n}2^n = n imes 2^n$$
于是整个问题的复杂度就是:$O(n2^{2n})$ 的了。
然而常数很小很小,应该很难构造一组卡到上界的数据,所以跑得还是比较快的。。。
1 2 #include <cmath> 3 #include <cstdio> 4 #include <cstring> 5 #include <iostream> 6 #include <algorithm> 7 using namespace std; 8 typedef long long LL; 9 #define N 12 + 5 10 #define SIZE 1 << 12 | 5 11 12 int n; 13 LL ans; 14 LL Fac[N]; 15 int A[SIZE], Cnt[SIZE]; 16 17 inline bool dfs(int state, int len) 18 { 19 if (len >> (n + 1)) return 1; 20 int cnt = 0, pos_1, pos_2; 21 for (int i = 0; i < (1 << n); i += len) 22 { 23 int _i = i + (len >> 1); 24 if ((A[i] & (len - 1)) || A[_i] != A[i] + (len >> 1)) 25 { 26 cnt ++; 27 if (cnt == 1) pos_1 = i; 28 else if (cnt == 2) pos_2 = i; 29 else return 0; 30 } 31 } 32 if ((state & 1) == 0 && cnt) return 0; 33 if ((state & 1) && !cnt) return 0; 34 if (!cnt) return dfs(state >> 1, len << 1); 35 if (cnt == 1) 36 { 37 swap(A[pos_1], A[pos_1 + (len >> 1)]); 38 bool ok = dfs(state >> 1, len << 1); 39 swap(A[pos_1], A[pos_1 + (len >> 1)]); 40 return ok; 41 } 42 if (cnt == 2) 43 { 44 bool ok = 1; 45 if ((A[pos_1] & (len - 1)) && (A[pos_2] & (len - 1))) return 0; 46 else if (A[pos_1] & (len - 1)) 47 { 48 swap(A[pos_1], A[pos_2 + (len >> 1)]); 49 if (A[pos_1] + (len >> 1) != A[pos_1 + (len >> 1)]) ok = 0; 50 if (A[pos_2] + (len >> 1) != A[pos_2 + (len >> 1)]) ok = 0; 51 if (ok) ok = dfs(state >> 1, len << 1); 52 swap(A[pos_1], A[pos_2 + (len >> 1)]); 53 return ok; 54 } 55 else if (A[pos_2] & (len - 1)) 56 { 57 swap(A[pos_2], A[pos_1 + (len >> 1)]); 58 if (A[pos_1] + (len >> 1) != A[pos_1 + (len >> 1)]) ok = 0; 59 if (A[pos_2] + (len >> 1) != A[pos_2 + (len >> 1)]) ok = 0; 60 if (ok) ok = dfs(state >> 1, len << 1); 61 swap(A[pos_2], A[pos_1 + (len >> 1)]); 62 return ok; 63 } 64 else 65 { 66 swap(A[pos_1], A[pos_2]); 67 if (A[pos_1] + (len >> 1) != A[pos_1 + (len >> 1)]) ok = 0; 68 if (A[pos_2] + (len >> 1) != A[pos_2 + (len >> 1)]) ok = 0; 69 if (ok) ok = dfs(state >> 1, len << 1); 70 swap(A[pos_1], A[pos_2]); 71 if (ok) return 1; 72 73 ok = 1; 74 swap(A[pos_1 + (len >> 1)], A[pos_2 + (len >> 1)]); 75 if (A[pos_1] + (len >> 1) != A[pos_1 + (len >> 1)]) ok = 0; 76 if (A[pos_2] + (len >> 1) != A[pos_2 + (len >> 1)]) ok = 0; 77 if (ok) ok = dfs(state >> 1, len << 1); 78 swap(A[pos_1 + (len >> 1)], A[pos_2 + (len >> 1)]); 79 return ok; 80 } 81 } 82 } 83 84 int main() 85 { 86 #ifndef ONLINE_JUDGE 87 freopen("3990.in", "r", stdin); 88 freopen("3990.out", "w", stdout); 89 #endif 90 91 scanf("%d", &n); 92 Fac[0] = 1; 93 for (int i = 1; i <= n; i ++) 94 Fac[i] = Fac[i - 1] * i; 95 for (int i = 1; i <= (1 << n); i ++) 96 { 97 scanf("%d", A + i - 1); 98 A[i - 1] --; 99 Cnt[i] = Cnt[i - (i & -i)] + 1; 100 } 101 for (int s = 0; s < (1 << n); s ++) 102 if (dfs(s, 2)) ans += Fac[Cnt[s]]; 103 printf("%lld ", ans); 104 105 #ifndef ONLINE_JUDGE 106 fclose(stdin); 107 fclose(stdout); 108 #endif 109 return 0; 110 }