题目简述
给定N 求1到N中有多少个幸运数字 幸运数字的定义为 这个数能被它二进制表示下1的个数整除
其中(1 ≤ N ≤ 1019)
--------------------------------------------------------------------------------------------------------------------------------
第二道数位DP题 在这里感谢一下 Gatevin 学长的讲解帮我克服了对数位DP的畏惧
这题我的做法和前面我写的那篇 windy数 的题解的思路差不多的
先从最低位到最高位处理一下只有当前位(当前位之前假设都是前导0)有限定的时候的方案数
( f数组的四位分别是 当前位是哪一位 这一位是0还是1 现在的集合中有几个1 现在的集合中的数modx的值)
然后再从最高位到最低位扫一遍即可
#include <bits/stdc++.h> using namespace std; const int N=70; unsigned long long n; int lim[N]; unsigned long long f[N][2][N][N]; int top; void prepare(int x) { memset(f,0,sizeof(f)); f[0][0][0][0]=1; f[0][1][1][1%x]=1; for(int i=0;i<top-1;++i) for(int j=0;j<=x;++j) for(int k=0;k<x;++k) { f[i+1][0][j][k]+=f[i][0][j][k]; f[i+1][0][j][k]+=f[i][1][j][k]; f[i+1][1][j+1][(k+(1ULL<<i+1))%x]+=f[i][0][j][k]; f[i+1][1][j+1][(k+(1ULL<<i+1))%x]+=f[i][1][j][k]; } } unsigned long long check(int x) { unsigned long long re=0; int tmp=0,cnt=0; for(int i=top-1;i>=0;--i) if(lim[i]) { if(x-cnt>=0) re+=f[i][0][x-cnt][(x-tmp)%x]; tmp=((1ULL<<i)+tmp)%x; ++cnt; } return re+(cnt==x&&!tmp); } int main() { scanf("%llu",&n); while(n) { lim[top++]=n&1; n>>=1; } unsigned long long ans=0; for(int i=1;i<=top;++i) { prepare(i); ans+=check(i); } printf("%llu",ans); return 0; }
时间: 2024-10-11 10:34:55