4 2 2 3 2 2
1 2HintThe first test case: the only solution is (2, 2). The second test case: the solution are (1, 2) and (2, 1).
题意:
给你k个数的和和k个数的最小公倍数。问你一共有多少满足条件的解。
开始SB了。以为是算组合数。纠结了半天。结果一看样例就凌乱了。
1,2。2,1。是不同的组合也就是说和排序有关。
以后一定要先看完样例 了。。。。
这下就简单了。用三维来表示状态。
dp[i][j][k]。表示长度为i。和为j。最小公倍数为k的方法数。
设a为解的第i+1个数。
那么状态转移就为
dp[i+1][j+a][lcm(a,k)]+=dp[i][j][k]。
lcm为最大公倍数。
由于三维开不下就只能用滚动数组了。
为了节约时间先预处理出1000以内任意两数的最小公倍数。注意一点。一个数学常识。如果这k个数的最小公倍数是
x。那么这k个数肯定都是x的因子。所以先预处理出x的因子。这样就可以有效的减少枚举范围。还有memset要慎用尤其是在这种数量级下。
详细见代码:
#include <iostream> #include<stdio.h> #include<string.h> #include<math.h> using namespace std; const int maxn=1010; const int mod=1000000007; int dp[2][maxn][maxn]; int lcm[maxn][maxn];//lcm[a][b]为a,b的最大公约数。 int fac[maxn];//存m的因子 int gcd(int a,int b) { int t; while(b) { t=a%b; a=b; b=t; } return a; } int main() { int n,m,k,cnt,i,j,p,q,e,f,t,v,mul,tt; for(i=1; i<maxn; i++) for(j=i; j<maxn; j++)//预处理出任意两数的最大公倍数 lcm[i][j]=lcm[j][i]=i*j/gcd(i,j); while(~scanf("%d%d%d",&n,&m,&k)) { cnt=0; for(i=1; i<=m; i++) { if(m%i==0) fac[cnt++]=i; } v=0; memset(dp,0,sizeof dp);//记得要初始化。wa了千百回。里面的初始化只初始化了部分 for(i=0; i<cnt; i++) dp[v][fac[i]][fac[i]]=1; for(i=1; i<k; i++) //len { for(e=i; e<=n; e++)//必须手动初始化。不然超时 for(f=0; f<cnt; f++) dp[v^1][e][fac[f]]=0; for(j=i; j<n; j++) //sum { for(p=0; p<cnt; p++) //mul { mul=fac[p]; if(!dp[v][j][mul]) continue; for(q=0; q<cnt; q++) //a[i] { tt=j+fac[q]; if(tt>n) break; t=lcm[mul][fac[q]]; dp[v^1][tt][t]+=dp[v][j][mul]; dp[v^1][tt][t]%=mod; } } } v^=1; } printf("%d\n",dp[v][n][m]); } return 0; }