ZOJ 3662 Math Magic(构造K个和为N且最小公倍数为M的正整数的方案数/dp)
来源:互联网 发布:网络短信在线发送 编辑:程序博客网 时间:2024/04/28 23:47
题目链接:
ZOJ 3662 Math Magic
题意:
求构造K个和为N且最小公倍数为M的正整数的方案数。
分析:
首先每个数都必须是M的约数,其次任意若干个数最小公倍数也必须是M的约数。
先预处理出1000以内的所有数的约数以及任意两个数的最小公倍数。
用dp[i][j][k]表示前i个数和为j最小公倍数是k的方案数。但是因为N和M及K的范围原因,这样直接用数组的话空间不够。
所以要用滚动数组。用dp[0][j][k]和dp[1][j][k]进行状态转化。
初始化:dp为0,但是dp[0][0][1]=1。
状态转移方程:
需要枚举上一状态的所有数字和,以及在这个和下的所有最小公倍数,然后枚举第i个数,就得到了前i个数的数字和以及最小公倍数, dp[now][x][y]=(dp[now][x][y]+dp[pre][i][divisor[M][j]])%mod;
其中
#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <climits>#include <cmath>using namespace std;const int MAX_N=1010;const int mod=(int)(1e9+7);int N,M,K;int dp[2][MAX_N][MAX_N];int LCM[MAX_N][MAX_N],divisor[MAX_N][MAX_N],cnt[MAX_N];inline int gcd(int a,int b){ return b==0?a:gcd(b,a%b);}inline int lcm(int a,int b){ return a/gcd(a,b)*b;}inline void GetLCM(){ //LCM[i][j]是i和j的最小公倍数 for(int i=1;i<=1000;i++){ for(int j=1;j<=i;j++){ LCM[i][j]=LCM[j][i]=lcm(i,j); } }}inline void GetDivisor(){ //divisor[i]保存i的约数,cnt[i]是i的约数个数 memset(cnt,0,sizeof(cnt)); for(int i=1;i<=1000;i++){ int tmp=0; for(int j=1;j*j<=i;j++){ if(i%j==0){ divisor[i][tmp++]=j; if(i/j!=j){ divisor[i][tmp++]=i/j; } } } cnt[i]=tmp; }}int main(){ freopen("Jin.txt","r",stdin); GetLCM(); GetDivisor(); while(~scanf("%d%d%d",&N,&M,&K)){ //memset(dp[0],0,sizeof(dp[0])); 如果用memset代替下面的四行会TLE,o(╯□╰)o for(int i=0;i<=N;i++){ for(int j=0;j<=M;j++){ dp[0][i][j]=0; } } dp[0][0][1]=1; int now=0,pre; for(int h=1;h<=K;h++){ pre=now; now^=1; //memset(dp[now],0,sizeof(dp[now])); 如果用memset代替下面的四行会TLE,o(╯□╰)o for(int i=0;i<=N;i++){ for(int j=0;j<=M;j++){ dp[now][i][j]=0; } } for(int i=h-1;i<=N;i++){ //前h-1个数的和是i for(int j=0;j<cnt[M];j++){ //枚举前h-1个数的和是i,LCM是divisor[M][j]的方案 if(dp[pre][i][divisor[M][j]]==0) continue; for(int r=0;r<cnt[M];r++){ //枚举第h个数 int x=i+divisor[M][r]; int y=LCM[divisor[M][j]][divisor[M][r]]; if(x>N||M%y!=0) continue; //前h个数的和x>n或者前h个数的最小公倍数y不是M的约数 dp[now][x][y]=(dp[now][x][y]+dp[pre][i][divisor[M][j]])%mod; } } } } printf("%d\n",dp[now][N][M]); } return 0;}
0 0
- ZOJ 3662 Math Magic(构造K个和为N且最小公倍数为M的正整数的方案数/dp)
- zoj 3662 dp (递推k个数 组成的和为n,最小公倍数为m的所有可能)
- HDU5482给大小为k的字符集,求长度为n且有m个非空字串的串数目,构造
- 求出所有的正整数对 使他们最大公约数为n,最小公倍数为m
- 一排N(最大1M)个正整数+1递增,乱序排列,第一个不是最小的,把它换成-1,最小数为a且未知求第一个被
- 将整数m拆分为n个数字的有序拆分方案数为C(m-1,n-1)
- NYOJ 746 - 正整数n划分为m段,求m段的最大乘积 【区间DP】
- 给定一个有n个正整数的数组A和一个整数sum,求选择数组A中部分数字和为sum的方案数
- 求n个中选m个的方案数(dp)
- 算法-生成和为S的N个正整数
- 随机生成N个和为S的正整数
- 【叶子函数分享四十】将一个正整数分解为m个2的n次方的和
- 【叶子函数分享四十】将一个正整数分解为m个2的n次方的和
- N个整数(数的大小为0-255)的序列,把它们加密为K个整数(数的大小为0-255).再将K个整数顺序随机打乱,使得可以从这乱序的K个整数中解码出原序列。设计加密解密算法,且要求K<=15*N.
- 在n个整数中选k个,使选出来的数的和为sum
- Pairs forming LCM (最小公倍数为n的数对)
- 趣题:均匀分布且和为常数的n个变量
- 【Codeforces Testing Round 12C】【DP 树状数组优化】Subsequences n个不同数,长度为m的LIS数
- R语言学习笔记1
- FastCgi与PHP-fpm关系
- HDU 1272 小希的迷宫
- eterna框架-介绍
- Kmeans算法详解及MATLAB实现
- ZOJ 3662 Math Magic(构造K个和为N且最小公倍数为M的正整数的方案数/dp)
- System.currentTimeMillis()
- 面试路之书单(0)
- springMVC 启用注解的问题
- POJ 3176 Cow Bowling
- SQL存储过程总结
- Oracle学习笔记(十五)——数据库(表)的逻辑备份与恢复
- 千里之行始于足下
- 如何搭建LNMP环境