1.题目描述:点击打开链接
2.解题思路:本题要求寻找k个正整数,它们的和恰好是N,它们的LCM恰好是M的解的个数。可以设置一个三维的dp来解决。用dp(i,j,k)表示选择i个数,它们的和恰好是j,它们的LCM恰好是k的个数。那么答案就是dp(k,n,m)。不过这里介绍一种利用状态压缩思想求解的方法。
通过题意可以发现,N,M的范围都比较小,不超过1000,而1000之内的所有数的不同素因子的种类数目不超过4个,这是因为2*3*5*7<1000,而2*3*5*7*11>1000。考虑到素因子种类数非常少的特点,我们可以考虑状态压缩。设dp(i,j,s)表示选择i个数,它们的和恰好是j,它们的不同素因子(这里考虑的素因子都是它的幂次恰好和M分解后对应的幂次相等的那个素因子)构成的集合为s时的解的个数。可以利用刷表法来求解,刷新公式如下:
dp(i,j+d[i],k|s[i])=dp(i,j+d[i],k|s[i])+dp(i-1,j,k);
上式中,d[i]表示M的第i个约数,s[i]表示第i个约数的“合格”的素因子构成的集合。假设M一共有L种不同的素因子,那么,k个整数的LCM恰好等于M就等价于dp(k,N,2^L-1)。2^L-1就是这L种素因子构成的集合的全集。这样,本题便可以被顺利的解决。
3.代码:
#include<iostream> #include<algorithm> #include<cassert> #include<string> #include<sstream> #include<set> #include<bitset> #include<vector> #include<stack> #include<map> #include<queue> #include<deque> #include<cstdlib> #include<cstdio> #include<cstring> #include<cmath> #include<ctime> #include<cctype> #include<functional> #pragma comment(linker, "/STACK:1024000000,1024000000") using namespace std; #define me(s) memset(s,0,sizeof(s)) #define rep(i,n) for(int i=0;i<(n);i++) typedef long long ll; typedef unsigned int uint; typedef unsigned long long ull; typedef pair <int, int> P; const int MOD=1e9+7; const int N=1050; int dp[110][N][1<<4]; int primes[N]; int vis[N]; int idx; void init() //筛素数 { me(vis); int m=sqrt(N+0.5); for(int i=2;i<=m;i++) if(!vis[i]) for(int j=i*i;j<N;j+=i) vis[j]=1; for(int i=2;i<N;i++) if(!vis[i]) primes[idx++]=i; } int main() { init(); int n,m,k; int num[5],p[5],d[233],type[233]; //p[i]表示M的第i个素因子,num[i]表示该素因子的幂次,d[i]表示M的第i个约数,type[i]表示第i个约数的“合格的”素因子构成的集合 int l,tot; //l表示M一共有l种不同的素因子,tot表示M一共有tot个约数 while(~scanf("%d%d%d",&n,&m,&k)) { l=0; for(int i=0,j=m;primes[i]<=j;i++)//对M进行分解 if(j%primes[i]==0) { p[l]=primes[i]; num[l]=0; while(j%primes[i]==0)j/=primes[i],num[l]++; l++; } tot=0; for(int i=1;i<=m;i++) //寻找M的所有约数 if(m%i==0) { d[tot]=i; type[tot]=0; int tmp=i,cnt; for(int j=0;j<l;j++) if(tmp%p[j]==0) { cnt=0; while(tmp%p[j]==0)tmp/=p[j],cnt++; if(cnt==num[j]) //如果该约数分解后,某一项的幂次和M对应的幂次相等,说明是一个合法的素因子,加入集合 type[tot]|=1<<j; } tot++; } int up=1<<l; //up表示M的不同素因子构成的全集 me(dp); for(int i=0;i<tot&&d[i]<=n;i++) dp[1][d[i]][type[i]]=1; //刷新时候需要的基础的解 for(int i=2;i<=k;i++) for(int j=1;j<=n;j++) for(int k=0;k<up;k++)//枚举所有集合 if(dp[i-1][j][k]) for(int kk=0;kk<tot&&d[kk]+j<=n;kk++)//枚举所有可能的约数,进行刷新 dp[i][j+d[kk]][k|type[kk]]=( dp[i][j+d[kk]][k|type[kk]]+dp[i-1][j][k])%MOD; printf("%d\n",dp[k][n][up-1]); } }
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-10-14 05:38:12