问题重述:
求解在区间[start, finish]之间的Round Number的数目。所谓Round Number指的是,二进制表示中0的位数大于等于1的位数的整数。
分析:
1.假如能够分别得到[0, finish] 和 [0, start - 1]区间内的Round Number的数目n1, n2, 那么问题的答案就是n1 - n2. 因此问题可以转化为求解小于n的Round Number的数目。
2.假设整数N的二进制表示为 1,n2,n3,n4,..nk, 则可以把小于N的roundnumber的数目分为两个部分
1)二进制表示位数小于k的Round Number的个数。
容易知道位数为j的Round Number的个数为 C[j-1][0] + C[j - 1][1] + ... + C[j - 1][j / 2 - 1]。
2)位数为k且小于N的Round Number的个数。
M小于N可以理解为:M和N的二进制表示在出现第一个不同位之前的所有位都相同,且mj = 0, nj = 1,(j表示第一个不同位的序号)。因此小于N的Round Number的数目可以通过如下算法计算:找到N中非零位nj = 1, 令mj=0, 且m1 = n1, m2 = n2,.., mj-1 = nj-1,这样只需要令剩余的k - j位的非零位的数目使M满足Round Number的条件即可。
例如对于N=1010010, 1)令M = 100xxxx, 那么M的后4位最多只能有2个1,因此满足该形式的M的个数有C[4][0] + C[4][1] + C[4][2]; 2)令M = 101000X, 则满足条件的M的个数为C[1][0] + C[1][1].
根据以上分析即可求解此问题。
AC代码
1 //Memory: 232K Time: 16MS 2 #include <iostream> 3 #include <cstring> 4 #include <cstdio> 5 6 using namespace std; 7 8 int c[64][64]; 9 int n[64]; 10 11 void init_c() 12 { 13 for (int i = 0; i < 64; i++) { 14 c[i][0] = 1; 15 c[i][i] = 1; 16 } 17 for (int i = 2; i < 64; i++) 18 for (int j = 1; j < i; j++) 19 c[i][j] = c[i - 1][j] + c[i - 1][j - 1]; 20 } 21 22 int a[40]; 23 int len = 0; 24 25 int roundNum (long long n) 26 { 27 len = 0; 28 while(n) { 29 a[len++] = n % 2; 30 n /= 2; 31 } 32 int ret = 0; 33 34 for (int i = 2; i < len; i++) { 35 for (int j = 0; j <= i / 2 - 1; j++) { 36 ret += c[i - 1][j]; 37 } 38 } 39 40 int cnt = 1; 41 for (int i = len - 2; i >= 0; i--) { 42 if (cnt > len / 2) break; 43 if ( a[i] ) { 44 for (int j = 0; j <= len / 2 - cnt; j++) 45 ret += c[i][j]; 46 cnt++; 47 } 48 } 49 50 return ret; 51 } 52 53 int main() 54 { 55 init_c(); 56 long long s, f; 57 cin >> s >> f; 58 cout << roundNum(f + 1) - roundNum(s) << endl; 59 return 0; 60 }