对于数字x,令F[x] = x二进制中1的个数
输入两个数字n(输入为二进制)和k,问有多少个满足①<=n;②刚好进行k次F[]计算后变为1
思路:
显然n<=2^1000但是第一次F[]计算之后一定会变成一个<=1000的数
先预处理1到1000每个数要变几次,然后对于刚好变k-1次的数x就相当于求出有多少个数满足
①<=n;②二进制刚好有x个1
这可以用数位DP或者组合数学解决
大概就是如果当前位是1的话把它变成0后面所有数字都可以01任选了,具体看代码
最后对于每个合法的x算出来的情况数全部加在一起就是答案
不过这样有两个坑,一个是k=0是答案一定为1,第二个是k=1时注意计算出的答案要-1
#include<stdio.h>
#include<string.h>
#define LL long long
#define mod 1000000007
int a[1005];
LL C[1005][1005];
char str[1005];
int Jud(int x)
{
int now, sum = 0;
while(x>1)
{
sum++;
now = 0;
while(x)
{
if(x%2==1)
now++;
x /= 2;
}
x = now;
}
return sum;
}
LL Sech(int x)
{
LL sum = 0;
int i, n, cnt = 0;
n = strlen(str+1);
for(i=1;i<=n;i++)
{
if(str[i]=='1')
{
if(cnt<=x)
sum = (sum+C[n-i][x-cnt])%mod;
cnt++;
}
}
if(cnt==x)
sum = (sum+1)%mod;
return sum;
}
int main(void)
{
LL ans;
int i, j, k;
for(i=1;i<=1000;i++)
a[i] = Jud(i);
C[0][0] = 1;
for(i=1;i<=1000;i++)
{
C[i][0] = 1;
for(j=1;j<=i;j++)
C[i][j] = (C[i-1][j-1]+C[i-1][j])%mod;
}
scanf("%s%d", str+1, &k);
if(k==0)
{
printf("1\n");
return 0;
}
ans = 0;
for(i=1;i<=1000;i++)
{
if(a[i]==k-1)
ans = (ans+Sech(i))%mod;
}
if(k==1)
ans--;
printf("%I64d\n", ans);
return 0;
}