zoj3662

Math Magic

Time Limit: 3 Seconds Memory Limit: 32768 KB

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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值