Description
小A有一个 1…2N 的排列 A[1…2N] ,他希望将 A 数组从小到大排序,小A可以执行的操作有
下面是一个操作事例:
第一次操作,执行第 3 种操作,交换
第二次操作,执行第 1 种操作,交换
第三次操作,执行第 2 种操作,交换
Input
第一行,一个整数 N 。
第二行,
Output
一个整数表示答案。
Sample Input
3
7 8 5 6 1 2 4 3
Sample Output
6
HINT
100% 的数据, 1≤N≤12。
题解
这道题的想法真的很妙。
首先你需要发现一个神奇的性质,就是对于每一种可能的方案,每一种操作的顺序并不影响操作结果,影响操作结果的仅为选择操作种类的集合。所以对于每一种可行的操作集合 S ,它对答案的贡献为
搜索的时候按照操作种类从小到大的顺序,因为只有这样才能在搜索比较大的区间时,小的区间已经有序。
假设当前搜索的操作种类为 t ,我们只需要判断对于所有无序的序列
My Code
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long ll;
ll fac[15], ans;
int n, N;
int a[10005];
int check(int pos, int k){
for(int i = 0; i < (1 << k); i ++){
if(a[pos + i] != a[pos] + i) return 0;
}
return 1;
}
void swp(int posx, int posy, int k){
for(int i = 0; i < (1 << k); i ++){
swap(a[posx + i], a[posy + i]);
}
}
void dfs(int t, int cur){
if(t == n){
ans += fac[cur]; return;
}
int m = 1 << t; int m2 = m + m;
int t1 = -1, t2 = -1;
for(int i = 0; i < N; i += m2){
if(!check(i, t + 1)){
if(t1 == -1) t1 = i;
else if(t2 == -1) t2 = i;
else return;
}
}
if(t1 == -1 && t2 == -1) dfs(t + 1, cur);
else if(t2 == -1){
swp(t1, t1 + m, t);
dfs(t + 1, cur + 1);
swp(t1, t1 + m, t);
}else{
for(int i = 0; i <= 1; i ++){
for(int j = 0; j <= 1; j ++){
swp(t1 + i * m, t2 + j * m, t);
if(check(t1, t + 1) && check(t2, t + 1)){
dfs(t + 1, cur + 1);
swp(t1 + i * m, t2 + j * m, t);
break;
}
swp(t1 + i * m, t2 + j * m, t);
}
}
}
}
int main(){
scanf("%d", &n);
N = 1 << n;
for(int i = 0; i < N; i ++){
scanf("%d", &a[i]);
}
fac[0] = 1;
for(int i = 1; i <= n; i ++){
fac[i] = fac[i - 1] * ll(i);
}
dfs(0, 0);
printf("%lld\n", ans);
return 0;
}
/*
3
3 6 1 2 7 8 5 4
6
*/