BZOJ3990 [SDOI2015]排序 【搜索】

题目

小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].

输入格式

第一行,一个整数N

第二行,2^N个整数,A[1..2^N]

输出格式

一个整数表示答案

输入样例

3

7 8 5 6 1 2 4 3

输出样例

6

提示

100%的数据, 1<=N<=12.

题解

我们可以大力猜想出操作的顺序可以是任意的
随便试试就会发现
仔细观察发现,所有的操作区间只有 完全包含关系 or 不相交
对于一个合法操作序列中的相邻两个操作(a)(b),我们尝试交换其顺序
①若(a)(b)不相交,那么显然无影响
②若(a)(b)相交,那么一定是包含关系,不妨设(|a| < |b|)
如果(a)的一个操作区间在(b)中,那么在操作(b)前后交换(a)中的两个区间显然不改变顺序
如果(a)的两个操作区间都在(b)中,那么在(b)操作前后这两个区间内的元素是不变的,我们只需在(b)操作之后找到原来的两个区间进行交换,最后的序列仍然不变
这就粗略地证明了

既然顺序无关,我们就可以从小枚举了
因为大区间操作无法影响其内部,所以我们每一次操作都要保证下一级区间内部一定是按+1递增顺序的
具体地,对于第(i)种操作,操作区间长度为(2^{i - 1}),那么我们找到第(i + 1)种操作的所有区间,如果其内部不是按+1递增的,那么这个区间一定要被操作

如果这样的区间数量(>3),显然我们是无法全部顾及的,直接返回
如果这样的区间数量为1,那么只需要交换这个区间内部
如果这样的区间数量为2,若存在合法方案,一定是交换这两个区间(2)个子区间的一个,共有(4)中情况,逐一检验即可

最后,如果一个操作集合(S)合法,那么将贡献(|S|!)的方案数

如果说每一层只会有一种情况合法的话,总的复杂度(O(n * 2^n))

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
using namespace std;
const int maxn = 13,maxm = 10000,INF = 1000000000;
inline int read(){
	int out = 0,flag = 1; char c = getchar();
	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
	return out * flag;
}
int a[maxm],n,N,chs;
LL ans,fac[maxn];
bool isorder(int l,int len){
	for (int i = 1; i < len; i++) if (a[l + i] != a[l + i - 1] + 1) return false;
	return true;
}
void Swap(int u,int v,int len){
	for (int i = 0; i < len; i++) swap(a[u + i],a[v + i]);
}
void dfs(int dep){
	if (dep > n){
		if (isorder(1,N)) ans += fac[chs];
		return;
	}
	int len = 1 << dep,x = 0,y = 0;
	for (int i = 1; i <= N; i += len){
		if (!isorder(i,len)){
			if (!x) x = i;
			else if (!y) y = i;
			else return;
		}
	}
	if (!x && !y) dfs(dep + 1);
	else if (x && !y){
		chs++;
		Swap(x,x + (len >> 1),(len >> 1));
		dfs(dep + 1);
		Swap(x,x + (len >> 1),(len >> 1));
		chs--;
	}
	else if (x && y){
		chs++;
		for (int i = 0; i < 2; i++)
			for (int j = 0; j < 2; j++){
				Swap(x + i * (len >> 1),y + j * (len >> 1),(len >> 1));
				if (isorder(x,len) && isorder(y,len))
					dfs(dep + 1);
				Swap(x + i * (len >> 1),y + j * (len >> 1),(len >> 1));
		}
		chs--;
	}
}
int main(){
	fac[0] = 1;
	for (int i = 1; i <= 12; i++) fac[i] = fac[i - 1] * i;
	n = read(); N = (1 << n);
	REP(i,N) a[i] = read();
	dfs(1);
	cout << ans << endl;
	return 0;
}

原文地址:https://www.cnblogs.com/Mychael/p/8855680.html