题面
题目描述
小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$ 认为两个操作序列不同,当且仅当操作个数不同,或者至少一个操作不同(种类不同或者操作位置不同).
下面是一个操作事例: $N=3$ , $A[1..8]=[3,6,1,2,7,8,5,4]$.
第一次操作,执行第 $3$ 种操作,交换$A[1..4]$和$A[5..8]$,交换后的$A[1..8]$为$[7,8,5,4,3,6,1,2]$.
第二次操作,执行第 $1$ 种操作,交换$A[3]$和$A[5]$,交换后的$A[1..8]$为$[7,8,3,4,5,6,1,2]$.
第三次操作,执行第 $2$ 种操作,交换$A[1..2]$和$A[7..8]$,交换后的$A[1..8]$为$[1,2,3,4,5,6,7,8]$.
输入输出格式
输入格式
第一行,一个整数$N$ ($n<=12$)
第二行,$2^N$个整数,$A[1..2^N]$
输出格式
一个整数表示答案
输入输出样例
输入样例
3 7 8 5 6 1 2 4 3
输出样例
6
代码
#include<bits/stdc++.h> using namespace std; inline int read(){ int ret=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9') {if (ch=='-') f=-f;ch=getchar();} while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar(); return ret*f; } long long n,s[10005],bin[30],jc[20],ans=0; bool check(int x,int len){//检查$[x,x+len−1]$这一段是不是严格按 1 递增 for (int i=x+1;i<=x+len-1;i++){ if (s[i]!=s[i-1]+1){ return 0; } } return 1; } void swapp(int x,int y,int len){//交换$[x,x+len-1]$和$[y,y+len-1]$两段 for (int i=1;i<=len;i++){ swap(s[x+i-1],s[y+i-1]); } } void dfs(int k,int cnt){//分割为$2^k$段,在此之前操作了cnt次 if (k==n+1&&check(1,bin[n])){//如果搜到底且有序那么计算当前操作的贡献并累计 ans+=jc[cnt]; return; } int tmp=0,t1=0,t2=0; for (int i=1;i<=bin[n];i+=bin[k]){//检测$2^k$段中的每一段是不是严格按 1 递增 if (!check(i,bin[k])){//记录不严格递增的 tmp++; if (tmp==1){ t1=i; } if (tmp==2){ t2=i; } if (tmp>2){//如果有 2 段以上不严格递增的,那么回溯 return; } } } if (tmp==0){//如果没有不严格递增的就继续搜索 dfs(k+1,cnt); return; } if (tmp==1){//如果有 1 段不严格递增的那么交换这一段中的前半段和后半段继续搜索 swapp(t1,t1+bin[k-1],bin[k-1]); dfs(k+1,cnt+1); swapp(t1,t1+bin[k-1],bin[k-1]); return; } if (tmp==2){//如果有两段不严格递增的 //交换第一段的前半段和第二段的前半段继续搜索 swapp(t1,t2,bin[k-1]); if (check(t1,bin[k])&&check(t2,bin[k])){ dfs(k+1,cnt+1); swapp(t1,t2,bin[k-1]); goto second; } swapp(t1,t2,bin[k-1]); //交换第一段的后半段和第二段的前半段继续搜索 swapp(t1+bin[k-1],t2,bin[k-1]); if (check(t1,bin[k])&&check(t2,bin[k])){ dfs(k+1,cnt+1); swapp(t1+bin[k-1],t2,bin[k-1]); goto second; } swapp(t1+bin[k-1],t2,bin[k-1]); second: //交换第一段的前半段和第二段的后半段继续搜索 swapp(t1,t2+bin[k-1],bin[k-1]); if (check(t1,bin[k])&&check(t2,bin[k])){ dfs(k+1,cnt+1); swapp(t1,t2+bin[k-1],bin[k-1]); return; } swapp(t1,t2+bin[k-1],bin[k-1]); //交换第一段的后半段和第二段的后半段继续搜索 swapp(t1+bin[k-1],t2+bin[k-1],bin[k-1]); if (check(t1,bin[k])&&check(t2,bin[k])){ dfs(k+1,cnt+1); swapp(t1+bin[k-1],t2+bin[k-1],bin[k-1]); return; } swapp(t1+bin[k-1],t2+bin[k-1],bin[k-1]); } } int main(){ bin[0]=1; for (int i=1;i<=20;i++){//预处理 2 的幂次方 bin[i]=bin[i-1]*2; } jc[0]=1; for (int i=1;i<=12;i++){//预处理阶乘 jc[i]=jc[i-1]*i; } cin>>n; for (int i=1;i<=bin[n];i++){ s[i]=read(); } dfs(1,0); cout<<ans; }