Alien 的排列题解

Description

原话:Alien 的思想真的很诡异。 对于一个 1..N 的排列,Alien 们会把它们全部+1,变成 2..N+1 的 Alien 排列,然后考虑这个排列的优美程度。我们称 Alien 排列的第 i 个数为 Ai,一个排列的是优美的当且仅当对于 i=1..N,i 可以整除 Ai。现在 Alien 给出一个 N,请你求一下 N 长度的优美排列个数。

简洁化题意

求出有多少 \(2\sim n+1\) 的排列 \(\{P_{n}\}\),使得对于所有 \(1\leq i\leq n\)\(i|P_{i}\)

对于 \(30\%\) 的数据 \(n\leq 10\)

对于 \(90\%\) 的数据 \(n\leq 3000\)

对于 \(100\%\) 的数据 \(n\leq 10^9\)

Solution

如果把 \(n+1\) 固定在第 \(1\) 个位置的话,只有一种排列,证明显然。

考虑将 \(n+1\) 与其他数交换。设把它与 \(k\) 交换,那么需要满足 \(k|n\land k\neq n\)。我们发现,交换后 \(k\) 跑到第一个位置了,而 \(k\) 显然不可能与 \(\geq k\) 的数交换,那么问题就转换成了一个 \(2\sim k\) 的排列的问题了。

\(f_{n}\)\(2\sim n\) 的合法排列方案数,则由上文得 \(f_{n}=\sum\limits_{d|n\land d\neq n}f_{d}\),边界是 \(f_{1}=1\)

使用朴素 dp 可以做到 \(\mathcal{O}(n^2)\)\(\mathcal{O}(n\sqrt{n})\)。可以使用记忆化搜索,时间复杂度应该是 \(\mathcal{O}(\sqrt{n\cdot d(n)})\)

Code

#include <bits/stdc++.h>
using namespace std;
namespace Milkcat {
	typedef long long LL;
	const int N = 1e6 + 5;
	LL n;
	unordered_map<LL, LL> f;
	LL dfs(LL n) {
		if (f[n]) return f[n];
		for (int i = 1; i * i <= n; i ++) {
			if (n % i) continue;
			f[n] += dfs(i);
			if (i * i != n && i != 1) f[n] += dfs(n / i);
		}
		return f[n];
	}
	int main() {
		cin >> n, n ++, f[1] = 1;
		cout << dfs(n) << '\n';
		return 0;
	}
}
int main() {
    int T = 1;
    while (T --) Milkcat::main();
    return 0;
}

Postscript

其实还有一个 \(\mathcal{O}(n^{\frac{1}{4}}+\log^2n)\) 的做法,我先不说。

posted @ 2023-06-15 19:45  喵仔牛奶  阅读(61)  评论(0)    收藏  举报