这道题最主要的就是问题的转化,转化完就是一道**题了。

仔细的分析一下,题目中要求“再次出现1,2,3...N的排列”,或者也就是问要进行多少次“置换操作”得到原排列,而我们知道任何置换都是可以写成循环乘积的形式的。

也就是比如对于

1->3
2->4
3->1
4->5
5->2

我们可以化简为

(1 3)(2 4 5)

而对于一个给定置换,问它要多少次才能再次得到原排列,显然只有这个置换分解出的的各个循环的长度是我们关心的。比如对于上例中的置换,我们只需要直到它分解出两个循环,长度为2与3即可断定其只需6次即可得到原排列。也就是所有循环的长度的最小公倍数次。
但这不就是暴力了吗?
但我们可以把循环的长度分解为质数之积,那么所有长度的最小公倍数就是每个质数的最大幂之积。(详见唯一分解定理)
所以我们就可以令dp[n][m]表示从第m个质数往后,值至多为n的方案数,然后dp就好了。

OVER

#include<bits/stdc++.h>
#define ll long long
const int N=1e5+5,INF=0x3f3f3f3f;
using namespace std;

int n,k=1,tmp;
int pri[200]={0,2};
ll ans[N];

inline int read()
{
    register int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*f;
}

int main()
{
    n=read();
    if(n==1)
    {
        printf("1");
        return 0;
    }
    for(int i=3;i<=n;i+=2)
    {
        pri[++k]=i;
        for(int j=2;j<k;j++)
        {
            if(i%pri[j]==0)
            {
                k--;
                break ;
            }
        }
    }
    ans[0]=1;
    for(int i=1;i<=k;i++)
    {
        for(int j=n;j>=pri[i];--j)
        {
            tmp=pri[i];
            while(tmp<=j)
            {
                ans[j]+=ans[j-tmp];
                tmp*=pri[i];
            }
        }
    }
    ll anss=0;
    for(int i=1;i<=n;i++) anss+=ans[i];
    printf("%lld",anss+1);
    return 0;
}