POJ 3252 Round numbers

http://poj.org/problem?id=3252

题意简述:输入的两个整数(包括)之间所有整数,转换为二进制后,0的位数大于等于1的位数的数字个数。

首先要求 l-r 中的数字个数,自然会想到求 [1->r]-[1->(l-1)];
但是要求的是转化为二进制后0的位数大于等于1的位数的数字个数,我们每次求的都是小于r或者l的数,
所以区间变为(1->r+1)-(1->l); 对于一个数x,它的二进制的位数为i 我们可以分两种情况:
1.如果一个数的二进制位数小于x的二进制位数,显然它是小于x的,而要使0的位数>=1的位数,就得使0的个数>=(i+1)/2, 我们可以预处理出所有数的二进制下一共i位,有>=j位是0的数的个数。然后从第一位枚举到i-1位,求出∑d[i-1,(i+1)/2]。

2.如果一个数的二进制位数等于i,那么需要逐位验证当前位是i还是0,统计出之前验证的0的个数,每次确定0的个数时要减去 前边的。

举个例子:
出自:
http://www.cnblogs.com/kuangbin/archive/2012/08/22/2651730.html
22=10110
如果要求 <=22的Round Numbers,也就是找出1-22有多少个二进制的0不少于1的数的个数。
22的二进制长度是5.
首先找长度比5小的Round Numbers(长度比5小的数肯定小于22啦)
长度为4的话,第一位必须是1,后面三位的话,可以有2个0,3个0
所以就是C(3,2)+C(3,3);
长度为3的Round Numbers,同理有 C(2,2);//注意不要把第一位1忘记了
长度为2的Round Numbers,有C(1,1)
长度为1的Round Numbers,有 0个
下面是找长度和22相同的Round Numbers。
首先第一位是1.
22的第二位是0,所以第二位不能为1,必须是0
第三位为0的话,(前面有了2个0,1个1),后面两位可以有1个0,2个0
C(2,1)+C(2,2)
接下来把第三位恢复为1,看第四位.假如第四位是0(前面有2个0,2个1),后面一位必须是0,所以是C(1,1)

#include<iostream>
#include<cstdio>
using namespace std;
int ans,k,s,e,a,b;
int f[35][35],d[35][35];
int work(int x,int bit){//x 当前数,bit位数 
    int sum=0;
    for (int i=1;i<bit;i++) sum+=d[i-1][(i+1)>>1];//二进制位数小于bit位,则一定是小于当前值x的 
    k=0;
    for (int i=bit-2;i>=0;i--)//向右移动i位可以验证第i+1位是0 or 1 
    if ((x>>i)&1){
       ans=((bit+1)>>1)-k-1;//剩下的0的个数 
        if (ans<0) ans=0;
        sum+=d[i][ans];
    }
    else k++;// k是0的个数 
    return sum;
} 
int main(){
    for (int i=0;i<=31;i++) f[i][0]=1;
    for (int i=1;i<=31;i++)
     for (int j=1;j<=i;j++)
     f[i][j]=f[i-1][j]+f[i-1][j-1];//求组合数 
    d[0][0]=1;
    for (int i=1;i<=31;i++)
     for (int j=i;j>=0;j--)
     d[i][j]=d[i][j+1]+f[i][j];
     cin>>s>>e;
     e++;
     while(s>>a) a++;//s 的二进制位数   
     while(e>>b) b++;
     cout<<work(e,b)-work(s,a);
    return 0;
}

f[i][j]存储组合数
d[i][j]表示二进制下一共i位,有>=j位是0的数的个数。
求区间[s,e]的RoundNumber可以转化[1,e+1)-[1,s) 设某数s在二进制下有a位,则1~a-1位的数很容易为:
∑d[i-1,(i+1)/2](1<=i<=a-1),其中第一位必须是1。 下面就是计算a位中比s小的符合要求的数:
若s上某一位是1(除了第一位),这一位前面已经有k个0,后边还有j位, 可以把这一位变成0,这样前面有k+1个0,
后边需要的0的个数至少为j=max(0,(a+1)/2-k-1),所以sum+=d[i][j]。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值