luoguP3322 [SDOI2015]排序

首先我们可以容易地知道任意区间交换的顺序对答案没有影响.

所以我们可以按照区间的长度进行搜索.

又因为每一种长度的区间只能交换一次,所以我们可以进行剪枝.

对于当前搜索区间的长度\(2^x\),我们可以对于每一个长度为\(2^{x+1}\)的区间,判断它是不是单调递增且相邻两数之间差\(1\),如果不是,则打上标记.

如果被标记了的区间超过了\(2\),那么无论如何都不能排好序了,直接回溯(想一想,为什么).

下面我们分标记区间的个数情况讨论:

  • \(0\)个:直接进行下一层的搜索.

  • \(1\)个: 直接将区间砍成两半,前面与后面交换就好了.

    原因很简单:上一层的区间一定都满足了要求,即单调递增且相邻两数之间差\(1\),所以只要交换前后两段即可(可以运用数学归纳法的思想).

  • \(2\)个:分别考虑两个区间头头交换,头尾交换,尾头交换,尾尾交换四种情况,若可行则继续搜索.

将每次可行的答案取阶乘累加至\(ans\)即可.

#include<bits/stdc++.h>
#define il inline
#define rg register
#define gi read<int>
using namespace std;
const int O = 1 << 12 | 15;
template<class TT>
il TT read() {
	TT o = 0,fl = 1; char ch = getchar();
	while (!isdigit(ch) && ch != '-') ch = getchar();
	if (ch == '-') fl = -1, ch = getchar();
	while (isdigit(ch)) o = o * 10 + ch - '0', ch = getchar();
	return fl * o;
}
int n, a[O], anc[15][O], ans;
il void swapp(int & x, int & y) {x ^= y ^= x ^= y;}
il void Swap(int S1, int S2, int x) {
	for (int i = 0; i < 1 << x - 1; ++i)
		swapp(anc[x + 1][S1 + i], anc[x + 1][S2 + i]);
}
il void dfs(int, int);
il void Doit(int S1, int S2, int b1, int b2, int x, int num) {
	Swap(S1, S2, x);
	for (int i = 1; i < 1 << x; ++i) {
		if (anc[x + 1][b1 + i] - anc[x + 1][b1] != i) return Swap(S1, S2, x);
		if (anc[x + 1][b2 + i] - anc[x + 1][b2] != i) return Swap(S1, S2, x);
	}
	dfs(x + 1, num + 1);
	Swap(S1, S2, x);
}
il void dfs(int x, int num) {
	if (x == n + 1) {
		for (int i = 1; i <= 1 << n; ++i) if (anc[x][i] ^ i) return ;
		int res = 1;
		for (int j = 2; j <= num; ++j) res *= j;
		ans += res;
		return ;
	}
	int cnt = 0, S1, S2;
	for (int i = 1; i <= 1 << n; i += 1 << x)
		for (int j = 1; j < 1 << x; ++j)
			if (anc[x][i + j] != anc[x][i] + j) {
				if (cnt == 2) return ;
				if (cnt) S2 = i;
				else S1 = i;
				++cnt; break;
			}
	for (int i = 1; i <= 1 << n; ++i) anc[x + 1][i] = anc[x][i];
	if (!cnt) return dfs(x + 1, num);
	if (cnt == 1) {
		Swap(S1, S1 + (1 << x - 1), x);
		dfs(x + 1, num + 1);
		return ;
	}
	Doit(S1, S2, S1, S2, x, num); Doit(S1 + (1 << x - 1), S2 + (1 << x - 1), S1, S2, x, num);
	Doit(S1, S2 + (1 << x - 1), S1, S2, x, num); Doit(S1 + (1 << x - 1), S2, S1, S2, x, num);
}
int main() {
	n = gi();
	for (int i = 1; i <= 1 << n; ++i) anc[1][i] = gi();
	dfs(1, 0);
	printf("%d\n", ans);
	return 0;
}
posted @ 2019-10-29 23:04  wuhan2005  阅读(70)  评论(0编辑  收藏