問題の説明
小さな彼が大きいと、アレイA小さいしたい、1-2 ^ N A [1..2 ^ N]が配置され、操作は、各操作をするために、一旦最大で実行することができる、小さなA N種類を用いて行うことができますすべての
I(1 <= I <= N)、操作左から右に、i番目の配列では、2 ^ {N-I + 1}のセグメントに分割され、各セグメントは、正確に2 ^ {I-1}番号含み、次いで2。A小さなが配列Aが小さいとすることができる思っている全体的な交換
その場合に限り、異なる操作の数、または少なくとも異なる演算(または異なる位置における異なるタイプの動作)小二つの異なる動作シーケンスAの多数のソートの動作の異なる配列。
以下は、インスタンスの操作です:
N = 3、A [1..8] = [3,6,1,2,7,8,5,4]。
[7,8,5,4,3に3つの操作、交換A [1..4]を実行し、A [5..8]、A [1..8]の交換の第1の動作、 6,1,2]。
第2動作、第一のタイプは、[7,8,3,4,5,6,1,2]などの操作、交換A [3]、A [5]、A [1..8]の交換を行います。
第3の動作は、[1,2,3,4,5に第2の操作、交換A [1..2]とA [7..8]、A [1..8]の交換で行われます6,7,8]。
入力形式
最初の行、N整数第二行目、2 ^ Nの整数、A [1..2 ^ N]。
出力フォーマット
答えを表す整数
サンプル入力
3
7 8 5 6 1 2 3 4
サンプル出力
6
データ範囲
データの100%、1 <= N <= 12。
解決
まず、我々がもし最初のために、大規模な操作に小さなのそれぞれについて説明し、2つのブロックがソートごとに交換注文しなければならないということも考えられる\(I \)動作モード、それぞれ長さのシーケンス2(\ ^ {I-1} \)ブロックは、その後長さを見て、順序付けられ\(2 ^ I \)単調増加ではないブロック。ブロックのない要求された数を超える2、解無しの場合。そうでなければ、数が1である場合、その交換ブロックの半分、2つの2交換の2~4分けの量で存在します。左端のブロック長を減算し、右端のブロックの値が記載された順序に等しい場合に命じ判定としては、自然界に配置してもよいです。
さらに、一連の操作のために、任意の二つの要素の交換は影響を受けませんので、長い間、それぞれが見つかったとして、\(N- \)の一連の動作を、増やす必要があります(N-!\)\スキームを。
コード
#include <iostream>
#include <cstdio>
#define N 5000
using namespace std;
int n=1,m,i,a[N];
long long ans,f[N];
int read()
{
char c=getchar();
int w=0;
while(c<'0'||c>'9') c=getchar();
while(c<='9'&&c>='0'){
w=w*10+c-'0';
c=getchar();
}
return w;
}
void dfs(int x,int sum)
{
int gap=(1<<(x-1)),gap1=gap<<1;
for(int i=1;i<=n&&x>1;i+=gap){
if(a[i+gap-1]-a[i]!=gap-1) return;
}
if(x==m+1){
ans+=f[sum];
return;
}
dfs(x+1,sum);
int op[5],cnt=0;
for(int i=1;i<=n;i+=gap1){
int j=(2*i+gap1-1)/2+1;
if(a[i+gap1-1]-a[i]!=gap1-1){
op[++cnt]=i;
op[++cnt]=j;
}
}
if(cnt>4) return;
for(int i=1;i<=cnt;i++){
for(int j=i+1;j<=cnt;j++){
for(int k=0;k<gap;k++) swap(a[op[i]+k],a[op[j]+k]);
dfs(x+1,sum+1);
for(int k=0;k<gap;k++) swap(a[op[i]+k],a[op[j]+k]);
}
}
}
int main()
{
m=read();
for(i=1;i<=m;i++) n*=2;
f[0]=1;
for(i=1;i<=12;i++) f[i]=f[i-1]*i;
for(i=1;i<=n;i++) a[i]=read();
dfs(1,0);
printf("%lld\n",ans);
return 0;
}