题目传送门
题目大意:
不知道怎么简要描述,看原题面吧。
思考过程:
我们首先来考虑如何求一个给定排列的排数,容易发现它就为这一排列中所有循环节(转着转着又能转回去的子串)长度的LCM(最小公倍数)。继续分析我们又能想到,几个数的LCM等于在他们之中出现过的所有质数的最高次幂乘积。然后题目就转化成了:对于一些质数的次幂之和如果小于N,排数就为这些质数的次幂之积,求这个积有多少可能的取值。
具体做法:
1.打一张质数表
2.在质数表上做背包,代表每个质数取几次幂,且始终保证和要小于等于N
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1100;
int book[maxn],prime[maxn];
int n,cnt=0;
long long ans=0;
long long dp[maxn][maxn];
void get_prime()
{
for(int i=2;i<=n;i++)
{
if(!book[i])
{
prime[++cnt]=i;
for(int j=1;j*i<=n;j++) book[i*j]=1;
}
}
}
int main()
{
scanf("%d",&n);
get_prime();
// for(int i=1;i<=cnt;i++) printf("%d ",prime[i]); printf("\n");
dp[0][0]=1;
for(int i=1;i<=cnt;i++)
{
for(int j=0;j<=n;j++) dp[i][j]+=dp[i-1][j];
for(int j=prime[i];j<=n;j*=prime[i])
for(int k=j;k<=n;k++)
dp[i][k]+=dp[i-1][k-j];
}
for(int j=0;j<=n;j++) ans+=dp[cnt][j];
printf("%lld\n",ans);
return 0;
}