题目意思:

给你1 - n的一个序列,你可以随便定义一个长度形成环,过了一段时间会回到1 - n这个状态,问这个时间的集合大小是多少?

Solution

显然这个回到起点的时间就是LCM全部环的长度,问题就变成了,n分隔成若干个数,求他们LCM的种类数。
枚举全部的可能一定会T掉,所以转换一下,LCM的种类数,也就是LCM不变的情况算一次即可,当你又枚举了不是一个不是素因子的数,一定会和之后的枚举重复,所以这类枚举是不必要的

使用筛法选出全部的素数,使用dp[i][j]代表前i个素因子总和为j的情况,
那么可以得到转移方程
观察这个式子,和完全背包问题是原模原样的,使用滚动数组 or 直接压掉第一维节约空间,最终合计dp[n][0~n]就是方案数。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3 + 7;

int cnt;
bool vis[N];
vector<int> prime;

void getprime() {
    memset(vis, true, sizeof vis);
    for (int i = 2; i < N; ++i) {
        if (vis[i])    prime.push_back(i), ++cnt;
        for (int j = 0; j < cnt; ++j) {
            if (i * prime[j] >= N)    break;
            vis[i * prime[j]] = 0;
            if (i % prime[j] == 0)    break;
        }
    }
}

ll dp[N], ans;

int main() {
    getprime();
    int n;
    scanf("%d", &n);
    dp[0] = 1;
    for (int i = 0; i < cnt; ++i) {
        for (int j = n; j >= prime[i]; --j)
            for (int k = prime[i]; k <= j; k *= prime[i])
                dp[j] += dp[j - k];
    }
    for (int i = 0; i <= n; ++i)
        ans += dp[i];
    printf("%lld\n", ans);
    return 0;
}