LGP3322题解
首先可以观察得到两个结论:
- 交换两个操作的操作顺序不会影响操作序列是否合法。
显然是正确的,模拟一下即可。
有这个结论之后就可以去考虑爆搜了。\(2^{24}\) 是能过的。
考虑从小的往大的搜。如果有一段内部没被排好序那么一定就寄了,或者某一段的开头不是 \(2^{i-1}\) 的倍数也寄。
然后看相邻两段,如果有超过二对没被排好序一定寄。如果有恰好二对 swap 一下即可,如果有恰好一对可以直接 swap。
最后需要乘一个阶乘。复杂度 \(O(2^{2n})\)。
#include<cstdio>
const int M=5005;
int m,n,ans,a[M],fac[15],len[M];
inline int abs(const int&a){
return a>0?a:-a;
}
inline void swap(int&a,int&b){
int c=a;a=b;b=c;
}
inline void IMP(const int&x,const int&y,const int&k){
const int&M=1<<k;for(int i=0;i<M;++i)swap(a[x<<k|i],a[y<<k|i]);
}
inline void DFS(const int&k,const int&cnt){
if(k==m)return void(ans+=fac[cnt]);
const int&N=1<<m-k,&M=1<<k;int sum(0);
for(int i=0;i<N;++i){
if(a[i<<k]%M)return;for(int j=1;j<M;++j)if(a[i<<k|j-1]>a[i<<k|j])return;
}
for(int i=0;i<N;i+=2){
if(a[(i+1<<k)-1]+1!=a[i+1<<k])++sum;
}
if(sum>2)return;
if(!sum)return DFS(k+1,cnt);
if(sum==1){
for(int i=0;i<N;i+=2){
if(a[(i+1<<k)-1]+1!=a[i+1<<k])IMP(i,i+1,k),DFS(k+1,cnt+1),IMP(i,i+1,k);
}
return;
}
if(sum==2){
int c1(-1),c2(-1);
for(int i=0;i<N;i+=2){
if(a[(i+1<<k)-1]+1!=a[i+1<<k])!~c1?c1=i:c2=i;
}
if(a[c1<<k]>a[c2<<k])swap(c1,c2);
if(a[(c1+1<<k)-1]+1==a[c2<<k]){
if(a[(c1+2<<k)-1]+1==a[c2+1<<k])IMP(c1+1,c2,k),DFS(k+1,cnt+1),IMP(c1+1,c2,k);
}
else{
if(a[(c2+1<<k)-1]+1==a[c1+1<<k]){
IMP(c1,c2,k),DFS(k+1,cnt+1),IMP(c1,c2,k);
IMP(c1+1,c2+1,k),DFS(k+1,cnt+1),IMP(c1+1,c2+1,k);
}
}
return;
}
}
signed main(){
scanf("%d",&m);n=1<<m;for(int i=0;i<n;++i)scanf("%d",a+i),--a[i];
fac[0]=1;for(int i=1;i<=m;++i)fac[i]=fac[i-1]*i;DFS(0,0);printf("%d",ans);
}