uva 11645 Bits

uva 11645 Bits

题目描述:一个数n的二进制表示中连续的11的个数记为f(n),例如f(111) = 2, f(1111) = 3,先给定n求ans = sum (f(i)) 0=<i <= n;
解决办法:逐位确定
举例说明:n = 1111, x1 = 11**, x2 = *11*, x3 = **11
所以答案可以分成如下3个部分,即前两位是11的个数,中间两位是11的个数,后两位是11的个数。
首先对于前两位是11的个数,也即x1情况下,此时,满足条件的数由后两位确定,所以一共有4个(0~11)
然后对于中间两位是11的个数,这个时候后面的取值受限于第一位,
但是我们可以分成两种情况来计算,前面的部分是小于n的对应位的也即取0,此时后面1位有2中取值方式,故有1*2个数;前面的部分刚好与n的对应部分相等即取1,此时后面只有0~1(1为n的最后几位的值)所以此时有:1 + 1个数, 此种情况下共有4个
最后对于后两位是11的个数,这个时候只取决于n的前两位的取值,所以有4种
综上3种情况,共有4+4+4 = 12
再看对于n = 11011:
同样可以分成x1 = 11***, x2 = *11**,x3 = **11*, x4 = ***11
计算x1:满足x1的情况只有011 + 1种,即:4种
计算x2: 满足x2的情况可以分成,前面的部分小于n对应位,此时只能取0,后面有2^2种取法,所以有4种,而对于前面部分等于n对应位,此时得到的数已经超过了11011所以此时是不存在的,故满足x2的共有4种
计算x3:同情况2,拆分成两个部分,前面两位小于11011的前两位,此时前面有3(0~11-1)种取法,后面可以取2^1种,故有3*2 = 6种;
前面两位与11011的前两位相等,由于已经超过原数,不存在
计算x4:由于最后两位和原数的最后两位一样,所以有110 + 1种取法,故而有7种取法
综上4种情况共有4 + 4 + 6 + 7 = 21。

归纳起来:对于位置i和i-1如果都为1,那么可以分成两部分:
l * 2^(i - 1) l = n>>i;
如果原数的i和i-1位都是1,那么返回,r + 1, r = n%(1<<(i-1));否则返回0
然后就是大数,只需要大数加。
最近写代码写的比较少,思维比较卡,代码写的比较搓。。。。。。在UVA上跑了,0.008s。。。。。。再优化一下,说不定可以跑到第一。。。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
LL f[65];
const int base = 10000;

class bigInt{
    private:
        int s, num[10];
    public:
        bigInt();
        bigInt(LL );
        friend bigInt operator+(const bigInt&, const bigInt &);
        void print()const ;
};

bigInt::bigInt(){
    s = 1;
    memset(num, 0, sizeof(num));
}

bigInt::bigInt(LL x){
    s = 0;
    memset(num, 0, sizeof(num));
    if(x==0)
        s = 1;
    while(x){
        num[s ++] = x%base;
        x = x/base;
    }
}

bigInt operator+(const bigInt &op1, const bigInt &op2){
    bigInt r;
    r.s = op1.s > op2.s ? op1.s : op2.s;
    for(int i = 0; i < r.s; i ++){
        r.num[i] = r.num[i] + op1.num[i] + op2.num[i];
        r.num[i + 1] = r.num[i]/base;
        r.num[i] = r.num[i]%base;
    }
    while(r.num[r.s]){
        r.num[r.s + 1] = r.num[r.s]/base;
        r.num[r.s] = r.num[r.s]%base;
        r.s ++;
    }
    return r;
}

void bigInt::print()const{
    printf("%d", num[s - 1]);
    for(int i = s - 2; i >= 0; i --){
        printf("%04d", num[i]);
    }
    printf("\n");
}

void init(){
    f[0] = 1;
    for(int i = 1; i != 64; i ++){
        f[i] = f[i - 1] + f[i - 1];
    }
}

int main(){
    init();
    int cas = 0;
    LL K, left, right;
    bigInt ans;
    ios::sync_with_stdio(false);

    while(cin>>K, K >= 0){
        int i;
        for(i = 63 ; i >= 0; i --){
            if(K&f[i]){
                break;
            }
        }
        left = 0, right = K;
        ans = 0;

        if(K&f[i]){
            right = K - f[i];
        }
        if(K&f[i - 1]){
            right = right - f[i - 1];
        }

        if((K&f[i]) && (K&f[i-1])){
            ans = right + 1;
        }
        i --;
        while(i > 0){
            left = left + left;
            left = left + ((K>>(i + 1))&1);
            if(K&f[i - 1]){
                right = right - f[i - 1];
            }
            if((K&f[i]) && (K&f[i-1]) ){
                ans = ans + left * f[i-1] + right + 1;
            }
            else{
                ans = ans + left * f[i-1];
            }
            i --;
        }
        printf("Case %d: ", ++cas);
        ans.print();
    }
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值