P3322 [SDOI2015]排序 暴搜

题意:

戳这里

分析:

通过手玩样例 , 我们可以发现以下几条性质:

  1. 一个合法的操作序列,各个操作其实是互不影响的,也就是说,只要我们发现了一组合法的操作序列,那么它的全排列是都可以的
  2. 对于一种排列,有且仅有一种合法的操作集合,因为任意一类操作是无法通过别的其他操作等价替换的

那么我们将问题转化成了枚举操作集合,判断是否可行

然后我们发现从小到大进行每个操作,进行第 (i) 种操作后,情况合法当且仅当每 (2^{i-1}) 个数都是单调递增的

对于样例而言:

进行第一次操作后的合法情况应该是 :(7,8,5 ,6,1,2,3,4)

对于第 (i) 种操作,分情况讨论:

  1. 若没有需要操作的区间,跳过
  2. 若有一个需要操作的区间,直接修改
  3. 若有两个操作的区间,分 4 种情况进行修改
  4. 若有两个以上的区间不合法,则无解

代码:

#include<bits/stdc++.h>

using namespace std;

namespace zzc
{
	inline long long read()
	{
		long long x=0,f=1;char ch=getchar();
		while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
		while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
		return x*f;
	}
	
	const int maxn = 4100;
	long long n,ans;
	long long a[maxn],pw[maxn],fac[maxn];
	
	bool check(int x,int l)
	{
		for(int i=1;i<l;i++) 
		if(a[x+i]!=a[x+i-1]+1) return false;
		return true;
	}
	
	void swap(int x,int y,int l)
	{
		for(int i=0;i<l;i++) std::swap(a[x+i],a[y+i]);
	}
	
	void dfs(int k,int num)
	{
		if(k>n)
		{
			ans+=fac[num];
			return ;
		}
		int tmp1=0,tmp2=0;
		for(int i=1;i<=pw[n];i+=pw[k])
		{
			if(!check(i,pw[k]))
			{
				if(!tmp1) tmp1=i;
				else if(!tmp2) tmp2=i;
				else return ;
			}
		}
		if(!tmp1&&!tmp2) dfs(k+1,num);
		else if(tmp1&&!tmp2)
		{
			swap(tmp1,tmp1+pw[k-1],pw[k-1]);
			dfs(k+1,num+1);
			swap(tmp1,tmp1+pw[k-1],pw[k-1]);
		}
		else
		{
			for(int x=0;x<=1;x++)
			{
				for(int y=0;y<=1;y++)
				{
					swap(tmp1+x*pw[k-1],tmp2+y*pw[k-1],pw[k-1]);
					if(check(tmp1,pw[k])&&check(tmp2,pw[k])) dfs(k+1,num+1);
					swap(tmp1+x*pw[k-1],tmp2+y*pw[k-1],pw[k-1]);
				}
			}
		}
	}
	
	void work()
	{
		n=read();
		fac[0]=pw[0]=1;
		for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i,pw[i]=pw[i-1]*2;
		for(int i=1;i<=pw[n];i++) a[i]=read();
		dfs(1,0);
		printf("%lld
",ans);
	}

}

int main()
{
	zzc::work();
	return 0;
}

原文地址:https://www.cnblogs.com/youth518/p/13958532.html