Yesterday, my teacher taught us about math: +, -, *, /, GCD, LCM... As you know, LCM (Least common multiple) of two positive numbers can be solved easily because of a * b = GCD (a, b) * LCM (a, b).
In class, I raised a new idea: "how to calculate the LCM of K numbers". It's also an easy problem indeed, which only cost me 1 minute to solve it. I raised my hand and told teacher about my outstanding algorithm. Teacher just smiled and smiled...
After class, my teacher gave me a new problem and he wanted me solve it in 1 minute, too. If we know three parameters N, M, K, and two equations:
1. SUM (A1, A2, ..., Ai, Ai+1,..., AK) = N
2. LCM (A1, A2, ..., Ai, Ai+1,..., AK) = M
Can you calculate how many kinds of solutions are there for Ai (Ai are all positive numbers). I began to roll cold sweat but teacher just smiled and smiled.
Can you solve this problem in 1 minute?
Input
There are multiple test cases.
Each test case contains three integers N, M, K. (1 ≤ N, M ≤ 1,000, 1 ≤ K ≤ 100)
Output
For each test case, output an integer indicating the number of solution modulo 1,000,000,007(1e9 + 7).
You can get more details in the sample and hint below.
Sample Input
4 2 2 3 2 2
Sample Output
1 2
题意: 给出N,M,K表示有K个数,他们的和为N,最小公倍数为M,让你求他们的方案数。
思路:感觉自己的dp还是不行,一开始想着从下一层的状态下,加上上一层能转移到当前状态的方案,感觉挺像状压,不知道是不是。但是后来发现这种情况太多了不好写,脑子转不过来去看了题解。才发现是从上一层直接累加到下一层能达到的情况,看完之后按照自己的想法写了一下,怎么写怎么超时,后来直接抄着写还是超时,最后才发现这道题时间卡很多地方。
注意:
①1000以内两个数的最小公倍数要先打好表
②打好表记录M的所有因子
③memset也会卡时间,主要是因为你只需要用到M的因子,而其他非M的因子的数不必维护
#include<stdio.h>
#include<string.h>
#define mod 1000000007
inline int __gcd(int a,int b){
return (b==0?a:__gcd(b,a%b));
}
inline int __lcm(int x,int y){
return x*y/__gcd(x,y);
}
int N,M,K;
int num[1000];
int cnt;
int lcm[1005][1005];
int dp[2][1005][1005];
int now;
void slove(){
memset(dp,0,sizeof(dp));
now=cnt=0;
for(int i=1;i<=M;i++) //M的因子表
if(M%i==0)
num[cnt++]=i;
for(int i=0;i<cnt;i++)
dp[0][num[i]][num[i]]=1;
for(int i=1;i<K;i++){
for(int j=i;j<=N;j++) //模拟memset将下一层几个用到的转移方案进行维护
for(int k=0;k<cnt;k++)
dp[now^1][j][num[k]]=0;
for(int j=i;j<N;j++){
for(int k=0;k<cnt;k++){
if(dp[now][j][num[k]]==0)
continue;
for(int p=0;p<cnt;p++){
int l=lcm[num[k]][num[p]];
int s=j+num[p];
if(s<=N){
dp[now^1][s][l]=(dp[now^1][s][l]+dp[now][j][num[k]])%mod;
}
}
}
}
now^=1;
}
}
int main(void){
for(int i=1;i<=1000;i++) //公倍数打表
for(int j=1;j<=1000;j++)
lcm[i][j]=__lcm(i,j);
while(~scanf("%d%d%d",&N,&M,&K)){
slove();
printf("%d\n",dp[now][N][M]);
}
return 0;
}