分析
我们先引入一个概念,置换。这里我简单的说一下,就是把 个元素的其它排列与原排列的映射关系。 可以用一个数组表示 。而这个道题就是给出一个置换,求有多少个不同的循环。为什么置换存在循环。不难发现,如果把每个数字看作一个节点,那么每个节点都有一个后驱节点和一个前驱节点,所以出度和入度都为 ,那么这个图必然构成若干个环。如果每个循环的长度为 ,那么在 次变化之后,置换一定变回原样。既然这道题要求有多少个循环,其实就是求 可以分解为多少个数的 的种类数。因为 。所以我们只需要在转移时,保证转移时 这样,可以不用考虑 的影响。定义状态 为考虑前 个质数,最后 的值 。那么答案为 。因为最后你总可以用 把后面填满,要注意 是从 开始枚举的,因为还有全是 的方案。其实把置换拆成循环的题还是比较多的 比如这道题 。
代码
#include<bits/stdc++.h> using namespace std; const int N = 1005; #define LL long long int read() {int x;scanf("%d",&x);return x;} LL f[2][N],g[N],n,vis[N]; void work(int x) { g[++g[0]] = 1; for(int i = 2;i <= x;i++) { if(!vis[i]) g[++g[0]] = i; for(int j = 1;j <= g[0] && g[j] * i <= x;j++) { vis[g[j] * i] = 1;if(!(g[j] % i)) break; } } } int main() { n = read(); work(n);f[0][0] = 1; // for(int i = 1;i <= g[0];i++) cout << g[i] << endl; for(int i = 1;i <= g[0];i++) { memset(f[i&1],0,sizeof(f[i&1])); for(int j = 0;j <= n;j++) { f[i&1][j] += f[i-1&1][j]; int k = g[i]; while(j >= k) { f[i&1][j] += f[i-1&1][j-k]; k = k * g[i]; } } } long long ans = 0; for(int i = 1;i <= n;i++) { ans += f[g[0]&1][i]; } cout << ans + 1 << endl; return 0; }