题意:
有个奇怪的钟,它有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;
}