正题
题目链接:https://www.luogu.com.cn/problem/P4161
题目大意
一个1∼n1\sim n1∼n的排列,f(x)f(x)f(x)表示在置换xxx下置换多少次才能回到原来的排列,求在所有置换下f(x)f(x)f(x)的所有可能的值的数量。
解题思路
一个置换的fff值就是这个置换的所有循环大小的lcmlcmlcm,问题转换为将nnn分成若干个数的和,求这些数的lcmlcmlcm可能的数量。
拆成的一个循环对于lcmlcmlcm的贡献我们也可以拆分质因数的形式,所以我们直接让拆分成的数一定是质数是不会影响答案的。
考虑dpdpdp,设fi,jf_{i,j}fi,j表示已经考虑了前iii个质数,这些数的和为jjj时的答案,那么有fi,j=∑fi,j−prijkf_{i,j}=\sum_{}f_{i,j-pri_j^k}fi,j=∑fi,j−prijk
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1100;
ll n,f[N][N],pri[N],cnt;
bool v[N];
int main()
{scanf("%lld",&n);for(ll i=2;i<=n;i++){if(!v[i])pri[++cnt]=i;for(ll j=1;j<=cnt&&i*pri[j]<=n;j++){v[i*pri[j]]=1;if(i%pri[j]==0)break;}}f[0][0]=1;for(ll i=1;i<=cnt;i++)for(ll j=0;j<=n;j++){f[i][j]=f[i-1][j];if(pri[i]>j)continue;for(ll k=pri[i];k<=j;k*=pri[i])f[i][j]+=f[i-1][j-k];}ll ans=0;for(ll i=0;i<=n;i++)ans+=f[cnt][i];printf("%lld",ans); return 0;
}