Description
windy学会了一种游戏。 对于1到N这N个数字,都有唯一且不同的1到N的数字与之对应。 最开始windy把数字按顺序1,2,3,……,N写一排在纸上。 然后再在这一排下面写上它们对应的数字。 然后又在新的一排下面写上它们对应的数字。 如此反复,直到序列再次变为1,2,3,……,N。 如: 1 2 3 4 5 6 对应的关系为 1->2 2->3 3->1 4->5 5->4 6->6 windy的操作如下
1 2 3 4 5 6
2 3 1 5 4 6
3 1 2 4 5 6
12 3 5 4 6
2 3 1 4 5 6
3 1 2 5 4 6
1 2 3 4 5 6
这时,我们就有若干排1到N的排列,上例中有7排。 现在windy想知道,对于所有可能的对应关系,有多少种可能的排数。
Input
一个整数,N。
Output
一个整数,可能的排数。
Sample Input
3
Sample Output
3
Data Constraint
Hint
100%的数据,满足 1 <= N <= 1000 。
.
.
.
.
.
.
分析
1、把原序列按照转化关系分组,由题面可以发现(1,2,3)在三行之后回到原状态,而(4,5)需要四行,(6)需要一行,得到小结论:由n个数构成的相互转化关系,其对应序列需要n行回到原状态
2、如何确定一个数经过转化后能够回到原数?注意题目原文有一句:对于1到N这N个数字,都有唯一且不同的1到N的数字与之对应,也就是说,如果构建一张图,以数字为点,转化关系为边,共有n个点n条边,且每个点入度和出度均为1,即每个点只与另外两个点相连,又得到小结论:这是一张由环(或单独的点)构成的图,那么一个数必定能转化回原数
3、不难看出,由两个含有m1,m2个数字的可转化序列(2中的环)所组成的序列,回到原状态需要LCM(m1,m2)(最小公倍数)行,而数字在序列中的位置关系不影响答案,如{(1->3,3->5,5->1),(2->4,4->2)}和{(1->2,2->3,3->1),(4->5,5->4)}是等价的
4、现在问题可抽象为:共有n个元素,将它分为m个集合,求每个集合元素个数的LCM可能出现的情况
5、对于每个LCM,可将它分解为质因数的乘积,L=(p1 ^ k1) (p2 ^ k2) ······* (pi ^ ki),而它的每个质因子都来自 “集合中的元素个数”,要用有限的元素个数得到尽可能多的LCM,就要使“集合中的元素个数”互质,这样LCM即为它们的乘积,不需要除以GCD导致损失,因此这个L能被凑出,当且仅当p1 ^ k1+p2 ^ k2+······+pi^ki<=n,注意可以将一个数单独分组,所以和可以小于n
6、用素数筛预处理出不大于n的素数
7、用f[i][j]表示前i个质数总和为j的情况数,得到转移方程f [i] [j] = Σ f [i-1] [j-pi^k] (k>=0 && j-pi^k>=0)
8、因为没有用第i个质数也是一种情况,所以f [i] [j] 一定包含f [k] [j] (0<=k<=i)的所有情况,可以把数组压缩到一维,即f [j] 表示总和为j的情况数
9、由于每一个i对应的f [j] 都由i-1对应的f [k] (k<=j)推得,循环j时需要从后向前
10、初始化f [0]=1
.
.
.
.
.
.
程序:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
long long n,k=1,f[1010],p[500];
int main()
{
scanf("%d",&n);
if (n==1)
{
printf("1");
return 0;
}
p[k]=2;
for (int i=3;i<=n;i+=2)
{
p[++k]=i;
for (int j=2;j<k;j++)
if (i%p[j]==0)
{
k--;
break;
}
}
f[0]=1;
for (int i=1;i<=k;i++)
for (int j=n;j>=p[i];j--)
{
int x=p[i];
while (x<=j)
{
f[j]+=f[j-x];
x*=p[i];
}
}
long long sum=0;
for (int i=1;i<=n;i++)
sum+=f[i];
printf("%lld",sum+1);
return 0;
}