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 }
posted on 2012-05-26 20:40  Dev-T  阅读(964)  评论(1编辑  收藏  举报