BZOJ 1025: [SCOI2009]游戏

1025: [SCOI2009]游戏

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 1 2 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


【输入样例二】
10

Sample Output

【输出样例一】
3

【输出样例二】
16


【数据规模和约定】
30%的数据,满足 1 <= N <= 10 。
100%的数据,满足 1 <= N <= 1000 。

——我是愉快的分隔符——

这道题嘛,看数字对应的循环节的长度,最后的排数就是所有循环节大小的最大公倍数。

所以这道题就转化为:已知a1+a2+a3+a4+…+ak=n。求LCM(a1,a2,a3,a4,…,ak)有多少种不同大小。

我们可以使用记忆化搜索的方法。先求出n一下的素数,然后用记忆化搜索把n,分解成若干个素数的若干倍。因为素数之间是和关系,要求素数的LCM,所以简单的一想是不会有重复的。设F[i][j]表示试到了第i个素数,n还剩余j。那么很简单发现F[i][j]=sigma{f[i][j-Prime[i]*k,k∈(0,+∞)且Prime[i]*k≤j]}。

下面是代码:

#include<cstdio>
using namespace std;
const int Maxn=1000;
int Prime[Maxn+10];
long long Ans[Maxn+10][Maxn+10]; 
int N; 
int n;
inline void getPrime(int x){
	bool isPrime=true;
	for (int i=1;i<=N;i++){
		if (x%Prime[i]==0){
			isPrime=false;
			break;
		}
	}
	if (isPrime) Prime[++N]=x;
}
long long getAns(int index,int left){
	if (Ans[index][left]>0) return Ans[index][left];
	if (index<=0) return 1;
	Ans[index][left]=getAns(index-1,left);
	for (int Times=Prime[index];Times<=left;Times*=Prime[index]) Ans[index][left]+=getAns(index-1,left-Times);
	return Ans[index][left];
}
int main(){
	Prime[N=1]=2;
	scanf("%d",&n);
	for (int i=3;i<=n;i++) getPrime(i);
	printf("%lld
",getAns(N,n));
	return 0;
}



原文地址:https://www.cnblogs.com/WNJXYK/p/4063962.html