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想知道,对于所有可能的对应关系,有多少种可能的排数。
【数据规模和约定】
100%的数据,满足 1 <= N <= 1000 。
如果一些数的最小公倍数为Z,而Z=x1^p1*x2^p2...xm^pm的话,当它们为x1^p1,x2^p2...时,它们的和最小。我们尝试尽量把这个最小化,因为达到最小化后,如果和小于等于N(不足可添1),就可以判定Z可以取到了。然后,可以发现,我们可以通过枚举xi^pi(质因数和其对应指数)来枚举Z(而且这样肯定不会重复),限制条件是和小于等于N。那么用dp[k][s]表示用前k个质数,枚举出来的所有Z的那个最小和为s的情况数。头疼,就写个记忆化好了。
1 #include<iostream>
2 #include<cstdio>
3 #include<cmath>
4 #include<algorithm>
5 #include<queue>
6 #include<cstring>
7 #define PAU putchar(‘ ‘)
8 #define ENT putchar(‘\n‘)
9 using namespace std;
10 const int maxn=200+10,maxm=1000+10,maxp=10000+10;
11 long long dp[maxn][maxm];int P[maxn],sz,n;bool pri[maxp];
12 void makepri(int n){
13 int lim=sqrt(n);memset(pri,true,sizeof(pri));
14 for(int i=2;i<=lim;i++)if(pri[i])for(int j=i*i;j<=n;j+=i)pri[j]=false;
15 for(int i=2;i<=n;i++)if(pri[i])P[++sz]=i;return;
16 }
17 long long calc(int k,int s){
18 if(dp[k][s]>=0)return dp[k][s];if(!k)return dp[k][s]=1;
19 dp[k][s]=calc(k-1,s);
20 for(int tmp=P[k];tmp<=s;tmp*=P[k])dp[k][s]+=calc(k-1,s-tmp);
21 return dp[k][s];
22 }
23 inline int read(){
24 int x=0,sig=1;char ch=getchar();
25 while(!isdigit(ch)){if(ch==‘-‘)sig=-1;ch=getchar();}
26 while(isdigit(ch))x=10*x+ch-‘0‘,ch=getchar();
27 return x*=sig;
28 }
29 inline void write(long long x){
30 if(x==0){putchar(‘0‘);return;}if(x<0)putchar(‘-‘),x=-x;
31 int len=0;long long buf[20];while(x)buf[len++]=x%10,x/=10;
32 for(int i=len-1;i>=0;i--)putchar(buf[i]+‘0‘);return;
33 }
34 void init(){
35 n=read();
36 makepri(n);memset(dp,-1,sizeof(dp));
37 write(calc(sz,n));
38 return;
39 }
40 void work(){
41 return;
42 }
43 void print(){
44 return;
45 }
46 int main(){init();work();print();return 0;}