POJ 3252
这题是我做的关于区间计数的第一道题,恩,纪念一下。
本题题意是:给定2个数,判断2个数之间(闭区间)存在多少个"round number"(即转换成2进制,0的个数多于1的个数)。
Sample Input
2 12
Sample Output
6
思路是:计算2-1以内的满足条件的数有多少个,以及12以内的数,再相减即可。
那么如何进行计数呢。比如:31.它的二进制码可以分解为[1,10000),[10000,11000),[11000,11100),[11100,11110),[11110,111111)这样就包括了所有情况,除了31本身需特判下。
分析[11110,11111),[11100,11110)很明显不存在这样的数,[11000,11100)只存在一个,那[10000,11000)呢。
[10000,10111] = [10000,11000),这样就可以转化成一个求组合数的问题了,即求后三位数中存在2个0,3个0的组合数之和为多少。
再比如[111000000,111100000) = [111000000,111011111],现在以倒数第5位为基准,因为前面存在3个1,和1个0,所以后面5位数中至少要有2个0,那么还剩下3个数不知道是0还是1.
因为1的个数要小于等于0的个数,所以剩下3个数中至少还需要2个0,故后5位数中求存在4个0,5个0的组合有多少种。
还有一点:可以先预处理一下1,10,100,1000,10000...数以内的满足要求的数有多少个。
以及求组合数C(n,n-k) = C(n,k)= C(n,k-1)*(n-k+1)/k;(k<=n/2).
好了,over!
1 #include<iostream> 2 #include<vector> 3 #include<fstream> 4 using namespace std; 5 vector<int> digit; 6 long long int data[40][40]; 7 long long int note[40]; 8 9 void ini() 10 { 11 data[0][0] = 1; 12 note[1] = 1;note[2] = 2; 13 for (long long int i(1); i<32; ++i) { 14 data[i][0] = data[i][i] = 1; 15 long long int end = i/2; 16 for (long long int j(1); j<=end; ++j) { 17 data[i][j] = data[i][i-j] = data[i][j-1]*(i-j+1)/j; //求组合数C(i,j) 18 } 19 if (i > 2) { 20 long long int start = (i-1)/2; 21 if ((i-1)%2)start++; 22 note[i] = note[i-1]+1; 23 for (long long int j(start); j<i-2; ++j) { 24 note[i] += data[i-2][j]; //预处理1,10,100,1000,10000...以内的满足要求的数有多少个 25 } 26 } 27 } 28 } 29 30 void change(int &start, int zero, int n) //寻找至少需要多少个0 31 { 32 for (; start<=n; ++start) { 33 if (start + zero >= n-start)return ; 34 } 35 start = 40; 36 } 37 38 long long int get_cnt(long long int x) //求x以内满足要求的数 39 { 40 if (x == 0)return 1; 41 int cnt = 0; 42 while (x) { 43 if (x&1) { 44 digit.push_back(cnt); 45 } 46 x >>= 1; 47 ++cnt; 48 } 49 int end = digit.size() - 1; 50 long long int ans = note[digit[end]+1]; 51 if (digit.size() != 1 && digit[end] + 1 >= digit.size()*2)ans++;//特判x本身这个数是不是round number 52 for (int i(0); i<digit.size()-1; ++i) { //区间计数 53 int zeros = ((digit[end] - digit[i]) - (end - i)) - (end - i) + 1; 54 int start = 0; //至少需要多少个0 55 change(start,zeros,digit[i]); 56 for (int j = max(start,0); j<=digit[i]; ++j) { 57 ans += data[digit[i]][j]; 58 } 59 if (i == digit.size()-2)ans--; //去掉最后一个重复情况 60 } 61 digit.clear(); 62 return ans; 63 } 64 65 int main() 66 { 67 ini(); 68 long long int fir,sec; 69 while (cin>>fir>>sec) { 70 long long int fir_cnt = get_cnt(fir-1); 71 long long int sec_cnt = get_cnt(sec); 72 cout<<sec_cnt - fir_cnt<<endl; 73 } 74 return 0; 75 }