搜索好題。spa
這一題中有不少顯而易見的結論。。。真的,沒發現或者沒有意會到會很麻煩。。。code
首先,操做序列能夠亂序,也就是說,對於一個操做集合(元素個數爲\(N\))對應成立的操做序列個數爲\(N!\)排序
而後咱們考慮從小到大分段交換,在這個過程當中間,咱們只要保證每一段是連續遞增的就能夠保證最終結果是連續遞增的。get
不妨設當前分的段爲\(K\)。it
咱們須要驗證一下在分段長爲\(K-1\)時交換的是否知足每一段是連續遞增。class
而後再考慮當前\(K\)段中,有幾段沒有遞增。搜索
摳出全部沒遞增的而後兩兩交換?集合
實測能夠,可是咱們要剪枝,由於這是一道搜索di
這裏又是一個剪枝:若是超過\(4\)段亂序就能夠退出了,交換不出來的。
總而言之,這一題就是一道分析加剪枝的毒瘤搜索題。
#include<bits/stdc++.h> using namespace std; #define int long long inline int read() { int f=1,w=0;char x=0; while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();} while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();} return w*f; } const int N=1LL<<15; int n,Fac[20],A[N],Ans; inline bool Check(int K) { for(int i=1;i<=1<<(n-K);i++) if(A[(i-1)*(1<<K)+1]+(1<<(K-1))!=A[(i-1)*(1<<K)+1+(1<<(K-1))]) return 0; return 1; } inline void SWAP(int i,int j,int K) { for(int k=1;k<=1<<K;k++) swap(A[i+k-1],A[j+k-1]); } inline void Dfs(int K,int Cnt) { int Tmp[5],Sum=0; if(K&&!Check(K)) return ; if(K==n) {Ans+=Fac[Cnt];return ;} Dfs(K+1,Cnt); for(int i=1;i<=1<<(n-K);i+=2) if(A[(i-1)*(1<<K)+1]+(1<<K)!=A[(i)*(1<<K)+1]) {if(Sum==4) return ;Tmp[++Sum]=i,Tmp[++Sum]=i+1;} if(!Sum||Sum>4) return ; for(int i=1;i<=Sum;i++) for(int j=i+1;j<=Sum;j++) { SWAP((Tmp[i]-1)*(1<<K)+1,(Tmp[j]-1)*(1<<K)+1,K); Dfs(K+1,Cnt+1); SWAP((Tmp[i]-1)*(1<<K)+1,(Tmp[j]-1)*(1<<K)+1,K); } } signed main(){ #ifndef ONLINE_JUDGE freopen("A.in","r",stdin); #endif n=read(),Fac[0]=1; for(int i=1;i<=12;i++) Fac[i]=Fac[i-1]*i; for(int i=1;i<=(1<<n);i++) A[i]=read(); Dfs(0,0);printf("%lld",Ans); }