这题不错,可惜我还是太弱了,没想到qwq。
看了网上大佬题解之后写的,对比了一下代码,好像我写的还是挺简洁的(逃,只是吞行比较多)。
因为直接用lcm的值做下标会超时,所以我们观察发现可以组成lcm为m的,其实只可能是m的因子。所以我们预处理出所有m的因子放到a数组里。然后开始DP:
dp[i][j][k]代表选前i个数,和为j,lcm为a[k]的方案数。假设LCM(a,b)=c,因为知道a和c求b不容易,而知道a和b求c很容易,所以这里我们会采用刷表法。
另外即使我们已经优化了,因为ZOJ卡时间比较紧,所以还得预处理任两个数的lcm。
具体细节请看代码:
#include<bits/stdc++.h> using namespace std; const int N=1e3+10; const int MOD=1e9+7; int n,m,p,cnt; int a[N],lcm[N][N],dp[2][N][40]; int gcd(int a,int b) { return b==0 ? a : gcd(b,a%b); } int main() { for (int i=1;i<=1000;i++) for (int j=1;j<=1000;j++) lcm[i][j]=i*j/gcd(i,j); while (scanf("%d%d%d",&n,&m,&p)==3) { cnt=0; for (int i=1;i<=m;i++) if (m%i==0) a[++cnt]=i; memset(dp,0,sizeof(dp)); for (int i=1;i<=cnt;i++) dp[1][a[i]][i]=1; for (int i=1;i<p;i++) { //填i个数 int now=i%2,nxt=now^1; memset(dp[nxt],0,sizeof(dp[nxt])); for (int j=1;j<=n;j++) //前i个数和为j for (int k=1;k<=cnt;k++) { //前i个数lcm为a[k] for (int t=1;t<=cnt;t++) //下个位置(i+1)填a[t] if (j+a[t]<=n && lcm[a[k]][a[t]]<=m) { int tmp=lower_bound(a+1,a+cnt+1,lcm[a[k]][a[t]])-a; dp[nxt][j+a[t]][tmp]+=dp[now][j][k]; dp[nxt][j+a[t]][tmp]%=MOD; } } } cout<<dp[p%2][n][cnt]<<endl; } return 0; }