原题链接
1025: [SCOI2009]游戏
Time Limit: 1 Sec Memory Limit: 162 MB
Submit: 1636 Solved: 1035
[Submit][Status][Discuss]
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
HINT
【数据规模和约定】
100%的数据,满足 1 <= N <= 1000 。
分析:题意的马甲有点吐艳啊……事实上稍加分析不难发现这些数字的关系其实是形成了一个或者多个环,而最终摆出来的排数就是这些环的长度的最小公倍数+1,那么问题便转化为了,若干数和为n,这些数可能的不同最小公倍数的个数。
本蒟蒻的数论很弱,一开始没忘整数的唯一分解上面想……所以一开始一直想的搜而且总感觉搜能行……后来肚子一饿再加上受某处指点,灵光一闪。其实这个问题好像很类似背包……
定义状态f(i,j)表示用前i个质数作因数,和为s的若干数能形成不同最小公倍数的个数。根据最小公倍数的性质,不难发现让那些质因子单独分开一定能构成其他分法的最小公倍数……(这个东西的理由俺有点说不清楚……然而各位自己手推一下其实是比较显然的……呐就酱吧!。◕‿◕。)那么转移就比较容易了……
当然先要把n以内的素数筛出来。
详情见代码。
#include<iostream>
#include<cstdio>
using namespace std;
bool np[1010];
int tot,pri[170];
typedef unsigned long long LL;
LL f[170][1010];
void sieve(int n){
for(int i=2;i*i<=n;i++)if(!np[i]){
for(int j=i*i;j<=n;j+=i)np[j]=1;
}
for(int i=2;i<=n;i++)if(!np[i])pri[++tot]=i;
}
int main(){
int n;
scanf("%d",&n);
sieve(n);
f[0][0]=1;
for(int i=1;i<=tot;i++){
for(int j=0;j<=n;j++)f[i][j]=f[i-1][j];
for(int j=pri[i];j<=n;j*=pri[i]){
//枚举那个质因子的几次方
for(int k=0;k<=n-j;k++){
f[i][k+j]+=f[i-1][k];
}
}
}
LL ans=0;
for(int i=0;i<=n;i++)ans+=f[tot][i];
/*这里……就算和不为n也可以加上若干个1让它变成n而且对lcm不影响……
虽然这个是我在发现样例都过不了的时候才加上的……*/
cout<<ans<<endl;
return 0;
}