您的位置:首页 > 其它

BZOJ 3990 Sdoi2015 排序 DFS

2015-04-16 13:38 225 查看
题目大意:给定一个长度为2^n的排列,有n个操作,第i个操作为【将序列分成2^(n-i+1)段,每段长2^(i-1),然后任选两段交换】,每个操作最多用一次,求有多少操作序列能把序列排出来

Orz dzy

首先我们很容易发现一个操作序列是否合法与序列的顺序是无关的

因此我们只需要确定某个操作序列中每个操作选不选就行了 那么这类操作序列对答案的贡献就是选择的操作数的阶乘

我们从小到大DFS,对于第i次操作我们将序列分成2^(n-i)段,每段长度2^i

我们找到序列中不是连续递增的段,如果这样的段超过2个,显然就废了

如果没有这样的段,就不需要执行这个操作

如果有一个这样的段,判断将这个段的前半部分和后半部分交换后是否连续递增,如果是就交换然后继续DFS

如果有两个这样的段,判断四种交换情况然后DFS

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M (1<<12)
using namespace std;
int n;
int a[M];
long long fac[15],ans;
void Swap(int a[],int b[],int len)
{
	int i;
	for(i=0;i<len;i++)
		swap(a[i],b[i]);
}
void DFS(int dpt,int cnt)
{
	if(dpt==n)
	{
		ans+=fac[cnt];
		return ;
	}
	int stack[3]={0,0,0},top=0;
	int i,j,temp=1<<dpt+1;
	for(i=0;i<1<<n;i+=temp)
	{
		if( a[i+(temp>>1)-1]+1!=a[i+(temp>>1)] )
		{
			if(top==2)
				return ;
			stack[++top]=i;
		}
	}
	if(top==0)
	{
		DFS(dpt+1,cnt);
		return ;
	}
	if(top==1)
	{
		i=stack[1];
		if( a[i+temp-1]+1==a[i] )
		{
			Swap(a+i,a+i+(temp>>1),temp>>1);
			DFS(dpt+1,cnt+1);
			Swap(a+i,a+i+(temp>>1),temp>>1);
		}
		return ;
	}
	if(top==2)
	{
		i=stack[1];j=stack[2];
		if( a[i+(temp>>1)-1]+1==a[j+(temp>>1)] && a[j+(temp>>1)-1]+1==a[i+(temp>>1)] )
		{
			Swap(a+i,a+j,temp>>1);
			DFS(dpt+1,cnt+1);
			Swap(a+i,a+j,temp>>1);
			Swap(a+i+(temp>>1),a+j+(temp>>1),temp>>1);
			DFS(dpt+1,cnt+1);
			Swap(a+i+(temp>>1),a+j+(temp>>1),temp>>1);
		}
		if( a[j+(temp>>1)-1]+1==a[i] && a[j+temp-1]+1==a[i+(temp>>1)] )
		{
			Swap(a+i,a+j+(temp>>1),temp>>1);
			DFS(dpt+1,cnt+1);
			Swap(a+i,a+j+(temp>>1),temp>>1);
		}
		if( a[i+(temp>>1)-1]+1==a[j] && a[i+temp-1]+1==a[j+(temp>>1)] )
		{
			Swap(a+j,a+i+(temp>>1),temp>>1);
			DFS(dpt+1,cnt+1);
			Swap(a+j,a+i+(temp>>1),temp>>1);
		}
		return ;
	}
}
int main()
{
	int i;
	cin>>n;
	for(i=0;i<1<<n;i++)
		scanf("%d",&a[i]);
	for(fac[0]=1,i=1;i<=n;i++)
		fac[i]=fac[i-1]*i;
	DFS(0,0);
	cout<<ans<<endl;
	return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: