hdu4028 The time of a day(dp,map优化、离散化节点)

题意:

有个奇怪的钟,它有n(1<=n<=40)个指针,编号为1~n。
第i根指针每过i秒后就会回到初始位置。
你现在可以任选其中一些指针,定义一天的时间长为所有指针都回到初始位置的间隔。

给你一个m,问从有多少种指针选择方案可以使一天的时间大于等于m。

数据范围:n<=40,m<=263-1

解法:

一天的时间就是选出的指针的编号的lcm
那么问题变为:
1到n这n个数,有多少个子集,满足子集内所有数的lcm大于等于m
//
暴力枚举子集,复杂度2^40,折半枚举也不顶用

dp:
d[i][j]表示前i个数,子集lcm为j的方案数
枚举j,那么d[i][lcm(i,j)]+=d[i-1][j]

第一感觉会发现,j这一维太大了(2^31),数组开不下
但实际上也能想到j这一维肯定也不是所有数都有,
看别人博客说只有4万多个,但是没找到是怎么测出来的

因为j不连续,所以j这一维套个map就行了

最后就是,这题n只有40,但是数据有超多组,
必须预处理答案,对于每组询问直接统计>=m的方案数

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=45;
map<int,int>d[maxm];
int n,m;
int lcm(int a,int b){
    return a/__gcd(a,b)*b;
}
signed main(){
    ios::sync_with_stdio(0);cin.tie(0);
    //预处理
    d[1][1]=1;
    for(int i=2;i<=40;i++){
        d[i]=d[i-1];//继承d[i-1]
        d[i][i]++;//只有自己一个数的时候
        for(auto p:d[i-1]){
            int lc=lcm(i,p.first);
            d[i][lc]+=p.second;
        }
    }
    //
    int T;cin>>T;
    int cas=1;
    while(T--){
        cin>>n>>m;
        int ans=0;
        for(auto p:d[n]){
            if(p.first>=m){
                ans+=p.second;
            }
        }
        cout<<"Case #"<<cas++<<": ";
        cout<<ans<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/107755190