链接
题解
先把图建出来,容易发现这是一张由点和环构成的图,每个环转回去需要的步数就是环的长度,多个环同时转回去的最小长度为(lcm(len_1, len_2, len_3, ... , len_n))。
又环的长度和为(n),问题可转化成把一个正整数(n)分解成若干数的和,求他们的(lcm)的可能方案数
把(lcm)分解质因数,考虑一堆数的(lcm)是他们出现过的质因数的最高次幂的乘积,为了不产生损耗,构造的时候直接用质数的幂来凑,这样(gcd(..) = 1),不会产生损耗
注意他们的和可以小于(n),因为剩下的可以用(1)补齐
于是问题转化为一个背包问题,设(dp[i][j])表示用了(i)个质数,总和为(j)的总方案数,(dp[i][j] = sum f[i - 1][j - p_i^k])
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = (int)1e3 + 10;
int n;
int prime[N], tot, f[N], ans[N];
bool vis[N];
void Prime (int n) {
for (register int i = 2; i <= n; ++i) {
if (!vis[i]) prime[++tot] = i;
for (register int j = 1; j <= tot; ++j) {
if (prime[j] * i > n) break;
vis[i * prime[j]] = 1;
if (i % prime[j] == 0) break;
}
}
}
signed main() {
cin >> n;
if (n == 1) return cout << 1, 0;
Prime(n);
ans[0] = 1;
for (register int i = 1; i <= tot; ++i)
for (register int j = n; j >= prime[i]; --j) {
int tmp = prime[i];
while (tmp <= j) {
ans[j] += ans[j - tmp];
tmp *= prime[i];
}
}
ans[0] = 0;
for (register int i = 1; i <= n; ++i) ans[0] += ans[i];
printf("%lld", ans[0] + 1);
return 0;
}