LGP3322题解

首先可以观察得到两个结论:

  1. 交换两个操作的操作顺序不会影响操作序列是否合法。

显然是正确的,模拟一下即可。

有这个结论之后就可以去考虑爆搜了。\(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);
}
posted @ 2022-07-25 08:33  Prean  阅读(16)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};