$SDOI2015$排序
搜索好题。
这一题中有很多显而易见的结论。。。真的,没发现或者没有意会到会很麻烦。。。
首先,操作序列可以乱序,也就是说,对于一个操作集合(元素个数为\(N\))对应成立的操作序列个数为\(N!\)
然后我们考虑从小到大分段交换,在这个过程中间,我们只要保证每一段是连续递增的就可以保证最终结果是连续递增的。
不妨设当前分的段为\(K\)。
我们需要验证一下在分段长为\(K-1\)时交换的是否满足每一段是连续递增。
然后再考虑当前\(K\)段中,有几段没有递增。
抠出所有没递增的然后两两交换?
实测可以,但是我们要剪枝,因为这是一道搜索
这里又是一个剪枝:如果超过\(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);
}