HDU_4028_The time of a day_动态规划

来源:互联网 发布:如何看待知乎 编辑:程序博客网 时间:2024/05/29 17:35

爽到爆!


题意:

有n个指针,第i个指针经过i秒会回到初始位置,当所有指针第一次同时回到初始位置时,定义过去了一天,要求一天的长度不小于M秒,要从n根指针中选出一部分满足要求,问有多少种选法。



Input
There are a lot of test cases. The first line of input contains exactly one integer, indicating the number of test cases.
  For each test cases, there are only one line contains two integers N and M, indicating the number of pointers and the lower bound for seconds of a day M. (1 <= N <= 40, 1 <= M <= 263-1)
 

Output
For each test case, output a single integer denoting the number of ways.


选择一些指针以后,一天的长度就是指针的最小公倍数,一共只有40个数字,可以用动态规划解决,二维DP,第一维是讨论前i个指针,第二维是当前的最小公倍数是多少,状态转移很简单。

第二维最小公倍数很大,用map来解决,相当于起到了离散化的作用。

但是每个指针选或者不选到40时会有2^40中方法,这么多种肯定不行,这也是用搜索不行的原因,因为刷表式的搜索必然会遍历这2^40个点,但事实上,连续的数字中有多少数是别的数的因数?在求最小公倍数的时候这样的小的数字的效果就消失了,拿数据说话,第40层一共只有36864个不同的公倍数。

这题要是我来做,很可能就放弃了,再次提醒自己,理论不是实际情况,测试才是真爱。


代码如下:

#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<cmath>#include<map>using namespace std;#define mxn 50int n;long long m;map<long long,long long> dp[mxn];long long gcd(long long x,long long y){return !y ? x : gcd(y,x%y);}long long lcm(long long x,long long y){return x/gcd(x,y)*y;}void insert(int id,long long now,long long pre){if(dp[id].find(now)==dp[id].end())dp[id][now]=0;dp[id][now]+=dp[id-1][pre];}void init(){map<long long,long long>::iterator it,ed;for(int i=0;i<mxn;++i)dp[i].clear();dp[0][1]=1;for(int i=1;i<=40;++i){ed=dp[i-1].end();for(it=dp[i-1].begin();it!=ed;++it){long long pre=(it->first);long long tem=lcm(pre,i+0LL);insert(i,pre,pre);insert(i,tem,pre);}}}int main(){init();int cs;scanf("%d",&cs);for(int CS=1;CS<=cs;++CS){scanf("%d%lld",&n,&m);printf("Case #%d: ",CS);long long ans=0;map<long long,long long>::iterator it,ed;ed=dp[n].end();for(it=dp[n].begin();it!=ed;++it)if((it->first)>=m)ans+=(it->second);if(m==1)--ans;printf("%lld\n",ans);}return 0;}


0 0