hdu4028 The time of a day

4028 The time of a day
题目大意:有个奇怪的钟有n(1<=n<=40)个指针编号1~n 每根指针每过其对应编号秒后回到初始位置,定义一天的时间长为k(1<=k<=n)个指针都回到初始位置的间隔,现在给你一个m(<2^63-1)问从有多少种方案可以使一天的时间大于等于m

分析:一天的时间计算很简单就是k个数的最小公倍数,如果枚举每一种选取方案那么需要每次询问O(2^n)肯定是不现实的,分析发现虽然方案数很多但是方案对应的最小公倍数却很小,因为有很多的最小公倍数是相同的,大概有4万多种。这样就可以dp了

dp[i][j]表示前i个指针中选取方案状态(最小公倍数)为j有多少种那么方程为dp[i][j]=dp[i-1][j](不选第i)+dp[i-1][k](选第i个 只有当lcm(k,i)==j才加)。

那么第二维很大怎么办STL map!!!!

 1 #include <iostream>
 2 #include<cstdio>
 3 #include <cstring>
 4 #include <map>
 5 typedef long long ll;
 6 using namespace std;
 7 map<ll,ll>dp[50];
 8 int n;
 9 ll m;
10 ll gcd(ll a,ll b)
11 {
12     return b==0?a:gcd(b,a%b);
13 }
14 ll lcm(ll a,ll b)
15 {
16     return a/gcd(a,b)*b;
17 }
18 void init()
19 {
20     dp[1][1]=1;
21     for(int i=2;i<=40;++i)
22     {
23         dp[i]=dp[i-1];
24         dp[i][i]++;
25         for(map<ll,ll>::iterator it = dp[i-1].begin();it!=dp[i-1].end();++it)
26         {
27             long long t = lcm(i,it->first);
28             dp[i][t]+=it->second;
29         }
30     }
31 }
32 ll getans()
33 {
34     ll ans=0;
35     for(map<ll,ll>::iterator it = dp[n].begin();it!=dp[n].end();++it)
36     {
37         if(it->first>=m)ans+=it->second;
38     }
39     return ans;
40 }
41 int main ()
42 {
43     init();
44     int t;
45     scanf("%d",&t);
46     for(int Case=1;Case<=t;++Case)
47     {
48         scanf("%d%I64d",&n,&m);
49         printf("Case #%d: %I64d
",Case,getans());
50     }
51 }
View Code
原文地址:https://www.cnblogs.com/shuzy/p/3511504.html