【洛谷P3322】排序

题目

题目链接:https://www.luogu.com.cn/problem/P3322
小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]\).

思路

首先有一个明显的结论:对于一组合法的交换方案,任意调换操作顺序的最终结果是一样的。
所以我们可以从小到大搜索,最终方案数乘上操作次数的阶乘。
假设我们现在搜索到交换两个长度为\(2^k\)的序列,那么我们就将这一个序列划分成若干个长度为\(2^{k+1}\)的序列,显然我们需要满足在交换完两个区间后,每一个长度为\(2^{k+1}\)的序列要为一个公差为\(1\)的递增序列,这样我们才可能可以把它排好序。
那么假设对于所有的长度为\(2^{k+1}\)的序列,仅有1个不满足递增且连续,那么我们只需将这个序列前后两端长度为\(2^k\)调换,如果此时满足递增且连续,就继续往下搜索,否则该方案一定不成立。
如果有2个序列不满足递增且连续,我们就将这2个长度为\(2^{k+1}\)的序列分成的4个长度为\(2^k\)的序列,一共有4种交换方法,全部尝试一遍即可。
时间复杂度\(O(n\times 2^{2n})\)

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N=5000;
int n,a[N];
ll ans;

ll fac(int x)
{
	ll s=1;
	for (ll i=2;i<=x;i++) s*=i;
	return s;
}

bool check(int l,int r)
{
	for (int i=l+1;i<=r;i++)
		if (a[i-1]!=a[i]-1) return 0;
	return 1;
}

void Swap(int i,int j,int k)
{
	for (int l=1;l<=k;l++,i++,j++)
		swap(a[i],a[j]);
}

void dfs(int x,int k)
{
	if (x>n)
	{
		if (check(1,(1<<n))) ans+=fac(k);
		return;
	}
	int cnt=0,pos[5];
	for (int i=1;i<=(1<<n);i+=(1<<x))
		if (!check(i,i+(1<<x)-1))
		{
			pos[++cnt]=i;
			if (cnt>2) return;
		}
	if (cnt==1)
	{
		Swap(pos[1],pos[1]+(1<<x-1),(1<<x-1));
		if (check(pos[1],pos[1]+(1<<x)-1)) dfs(x+1,k+1);
		Swap(pos[1],pos[1]+(1<<x-1),(1<<x-1));
	}
	else if (cnt==2)
	{
		Swap(pos[1],pos[2],(1<<x-1));
		if (check(pos[1],pos[1]+(1<<x)-1) && check(pos[2],pos[2]+(1<<x)-1)) dfs(x+1,k+1);
		Swap(pos[1],pos[2],(1<<x-1));
		
		Swap(pos[1],pos[2]+(1<<x-1),(1<<x-1));
		if (check(pos[1],pos[1]+(1<<x)-1) && check(pos[2],pos[2]+(1<<x)-1)) dfs(x+1,k+1);
		Swap(pos[1],pos[2]+(1<<x-1),(1<<x-1));
		
		Swap(pos[1]+(1<<x-1),pos[2],(1<<x-1));
		if (check(pos[1],pos[1]+(1<<x)-1) && check(pos[2],pos[2]+(1<<x)-1)) dfs(x+1,k+1);
		Swap(pos[1]+(1<<x-1),pos[2],(1<<x-1));
		
		Swap(pos[1]+(1<<x-1),pos[2]+(1<<x-1),(1<<x-1));
		if (check(pos[1],pos[1]+(1<<x)-1) && check(pos[2],pos[2]+(1<<x)-1)) dfs(x+1,k+1);
		Swap(pos[1]+(1<<x-1),pos[2]+(1<<x-1),(1<<x-1));
	}
	else dfs(x+1,k);
}

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=(1<<n);i++)
		scanf("%d",&a[i]);
	dfs(1,0);
	printf("%lld",ans);
	return 0;
}
posted @ 2020-02-13 20:09  stoorz  阅读(184)  评论(0编辑  收藏  举报