暴力出正解系列,直接开搜
注意到操作顺序不影响答案,因此从小到大安排,最后统计答案时增加交换次数的阶乘就行了
如果一种状态已经开始选择第
i
i
i种操作,那么必须保证所有长度为
2
i
−
1
2^{i - 1}
2i−1的块(从第一位开始,每块首尾相接)内部已经排好序了。而如果有2个以上长度为
2
i
2^{i}
2i的内部没有排好序的块,就不能通过一次操作将其排好序。而在其之后的操作也不能把没有排好序的块复原。
其实第一个剪枝就能得75分了qwq
#include<cstdio>
#include<algorithm>
using namespace std;
int n, Ans;
int a[1 << 13];
int fac[13];
inline int read(){
int k = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){k = k*10 + ch - '0'; ch = getchar();}
return k * f;
}
inline bool Check(int t){
if(!t) return true;
int Len = (1 << (t - 1));
for(int i = 1; i <= (1 << n); i += Len * 2)
if(a[i] + Len != a[i + Len]) return false;
return true;
}
inline void Swap(int l1, int l2, int Len){
for(int i = 0; i < Len; i++){
swap(a[l1 + i], a[l2 + i]);
}
}
void dfs(int t, int num){
if(!Check(t)) return;
if(t == n){
Ans += fac[num];
return;
}
int tot = 0, w[10] = {0};
int Len = (1 << t);
for(int i = 1; i <= (1 << n); i += Len * 2){
if(a[i] + Len != a[i + Len]){
if(tot == 4) return; //交换不过来了
w[++tot] = i;
w[++tot] = i + Len;
}
}
for(int i = 1; i <= tot; i++){
for(int j = i + 1; j <= tot; j++){
Swap(w[i], w[j], Len);
dfs(t + 1, num + 1);
Swap(w[i], w[j], Len);
}
}
dfs(t + 1, num); //不做
}
int main(){
n = read();
for(int i = 1; i <= (1 << n); i++){
a[i] = read();
}
fac[0] = 1;
for(int i = 1; i <= n; i++){
fac[i] = fac[i - 1] * i;
// printf("%d ", fac[i]);
}
dfs(0, 0);
printf("%d", Ans);
return 0;
}