P3322 [SDOI2015]排序 题解

注意这题操作位置不同的意思不是在数组上不同的位置操作,而是两个方案中操作序列上某个位置的操作不同……

首先有个结论,就是如果我们能用 \(x\) 次操作完成排序,那么这 \(x\) 次操作可以任意交换,对答案的贡献就是 \(x!\)

因此考虑区间 \(i\) 从小到大枚举操作,由于是从小到大,因此必须要保证第 \(i\) 次操作后所有长度为 \(2^i\) 的区间都排了序且相邻两项差值为 1,否则后面的操作肯定不能再影响这个小区间。

考虑 dfs 处理,下面以 \(i=1\) 为例,剩下的情况可以在递归时修改数列变成 \(i=1\)

接下来考虑什么情况下能够通过一次操作完成上面说的排序,注意到只有以下两种情况且只能有其中一个情况

  1. 只有一个区间逆序,剩下的全满足条件。
  2. 有两个区间顺序,但是这两个区间不满足差值为 1,剩下的全满足条件。

这两个条件都可以 \(O(n)\) 扫一遍判定,如果都不满足这次操作就不用动,都满足那就非法。

如果满足第一个条件,将这个区间内的两个数都交换即可。

如果满足第二个条件,设这两个区间为 \((a+2,a+1),(a,a+3)\)(也只可能是这种情况),此时我们有两种方式,交换 \(a,a+2\) 或者交换 \(a+1,a+3\)

这样我们得到了一个可以往下递归的数列,至于递归时显然要满足 \(a_i\equiv i\pmod{2}\),因此我们每两个区间合并成一个数,新数的大小就是这个区间内偶数除以 2 或者奇数加一之后除以 2,这样就可以递归了。

Code:

/*
========= Plozia =========
	Author:Plozia
	Problem:P3322 [SDOI2015]排序
	Date:2022/10/26
========= Plozia =========
*/

#include <bits/stdc++.h>
typedef long long LL;

const int MAXN = 20 + 5, MAXS = (1 << 20) + 5;
int n, a[MAXS];
LL ans = 0, fact[MAXN];

int Read()
{
	int sum = 0, fh = 1; char ch = getchar();
	for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = (sum << 3) + (sum << 1) + (ch ^ 48);
	return sum * fh;
}
void Swap(int &x, int &y) { int t = x; x = y; y = t; }

void dfs(int now, int cnt)
{
	if (now == n + 1) { ans += fact[cnt]; return ; }
	int m = 1 << (n - now + 1), opt = 0, flag = 0, f1 = 0, f2 = 0, p1 = 0, p2 = 0;
	for (int i = 1; i <= m; ++i)
		if (a[i] % 2 != i % 2) { ++opt; if (f1 == 0) f1 = i; else f2 = i; }
	if (opt > 2) return ; if (opt == 2) Swap(a[f1], a[f2]);
	for (int i = 1; i <= m; i += 2)
		if (a[i] != a[i + 1] - 1) { ++flag; if (p1 == 0) p1 = i; else p2 = i; }
	if (opt && flag) return ; if (flag > 2) return ;
	if (flag == 0)
	{
		for (int i = 1; i <= (m >> 1); ++i) a[i] = ((a[(i << 1) - 1] + 1) >> 1);
		if (opt) dfs(now + 1, cnt + 1);
		else dfs(now + 1, cnt); return ;
	}
	int tmp[MAXS]; for (int i = 1; i <= m; ++i) tmp[i] = a[i];
	Swap(tmp[p1], tmp[p2]); for (int i = 1; i <= (m >> 1); ++i) a[i] = ((tmp[(i << 1) - 1] + 1) >> 1);
	dfs(now + 1, cnt + 1); Swap(tmp[p1], tmp[p2]); Swap(tmp[p1 + 1], tmp[p2 + 1]);
	for (int i = 1; i <= (m >> 1); ++i) a[i] = ((tmp[(i << 1) - 1] + 1) >> 1);
	dfs(now + 1, cnt + 1);
}

int main()
{
	n = Read(); for (int i = 1; i <= (1 << n); ++i) a[i] = Read();
	fact[0] = 1; for (int i = 1; i <= n; ++i) fact[i] = fact[i - 1] * i;
	dfs(1, 0); printf("%lld\n", ans); return 0;
}
posted @ 2022-10-26 11:30  Plozia  阅读(29)  评论(0编辑  收藏  举报