链接
题目描述
定义圆数为二进制下0的数位不小于1的数位,求l~r中的圆数个数
样例输入
2 12
样例输出
6
思路
数位DP
设
f
i
,
j
f_{i,j}
fi,j二进制下填到第i位,目前有j个0的情况
那么
f
i
,
j
=
f
i
−
1
,
j
+
f
i
−
1
,
j
−
1
f_{i,j} = f_{i-1,j}+f_{i-1,j-1}
fi,j=fi−1,j+fi−1,j−1
然后按位去枚举填0或填1,特判前导0就好了
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int s[55], l, r, tot, s0, s1, f[55][55];
int solve(int n)
{
int ans = 0;
tot = 0;
s0 = s1 = 0;
memset(s, 0, sizeof(s));
if(n == 0) return 0;
while(n) {
s[++tot] = n % 2;
n /= 2;
}//转成二进制
for(int i = tot; i >= 1; --i)
{
if(s[i] && i != tot)
{
s0++;
for(int j = 0; j <= i - 1; ++j)
if(j + s0 >= tot - j - s0) ans += f[i - 1][j];
s0--;
}//当前位改成填0
if(i != tot)
{
for(int j = 0; j <= i - 1; ++j)
if(j >= i - j) ans += f[i - 1][j];
}//当前位不变
s0 += !s[i];
s1 += s[i];
}
if(s0 >= s1) ans++;//n自身是否为圆数
return ans;
}
int main()
{
scanf("%d%d", &l, &r);
f[0][0] = 1;
for(int i = 1; i <= 35; ++i) {
f[i - 1][0] = 1;
for(int j = 1; j <= i; ++j) {
f[i][j] = f[i - 1][j] + f[i - 1][j - 1];
}
}//递推求
printf("%d", solve(r) - solve(l - 1));
}