hihoCoder挑战赛28题目2 : 二进制翻转

Posted 神犇(shenben)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hihoCoder挑战赛28题目2 : 二进制翻转相关的知识,希望对你有一定的参考价值。

 

题目2 : 二进制翻转

时间限制:20000ms
单点时限:1000ms
内存限制:256MB

描述

定义函数 Rev(x) 表示把 x 在二进制表示下翻转后的值

例如:

Rev(4)=1,因为 4 等于(100)B,翻转后是 (001)B,也就是 1

Rev(6)=3,因为 6 等于(110)B,翻转后是 (011)B,也就是 3

定义 Cnt(x) 表示 x 在二进制表示下 1 的个数,求:

技术分享

输入

仅一行,一个非负整数 n

0 ≤ n ≤ 1015

输出

仅一行:一个非负整数表示答案

样例输入
2
样例输出
3

 

/*
    Name: rev x& count num 1 of x in binary
    Copyright: @2017 shenben
    Author: shenben
    Date: 25/04/17 16:47
    Description:
//枚举当前要计算的数的位数,考虑从左右往中间dp
//f[i][j][k][cmp1][cmp2]表示考虑了前 i 位和后 i 位,前 i 位需要 j 个进位,后 i 位的进位为 k,前 i 位和后 i 位和上限的大小情况分别是 cmp1,cmp2
//转移时,同时枚举第 i+1 位和倒数第 i+1 位的值,枚举该位最后是否是 1 ,根据这个判断是否需要进位即可
//时间复杂度 O((logn)^2)
//(来自官方题解)     
*/
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=55;
ll n,ans;int m,dig[N];
ll f[N][N][2][3][2];
ll s[N][N][2][3][2];
inline int fun(int b,int x,int y){
    if(x==y) return b;
    if(x<y) return 2;
    return 0;
}
void solve(){
    memset(f,0,sizeof f);
    memset(s,0,sizeof s);
    if(m==1){ans++;return ;}
    f[1][1][0][fun(1,0,dig[1])][0]=1;
    f[1][0][0][fun(1,1,dig[1])][1]=1;
    s[1][1][0][fun(1,0,dig[1])][0]=2;
    s[1][0][0][fun(1,1,dig[1])][1]=2;
    for(int i=2;i<=m/2;i++){
        for(int j=0;j<=m;j++){
            for(int a=0;a<2;a++){
                for(int b=0;b<3;b++){
                    for(int c=0;c<2;c++){
                        ll k=f[i-1][j][a][b][c];
                        ll l=s[i-1][j][a][b][c];
                        if(!k) continue;
                        //0,0
                        f[i][0][a|(dig[m-i+1]==1)][fun(b,0,dig[i])][0]+=k;
                        s[i][0][a|(dig[m-i+1]==1)][fun(b,0,dig[i])][0]+=l;
                        //0,1
                        f[i][j+1][a|(dig[m-i+1]==1)][fun(b,1,dig[i])][c]+=k;
                        s[i][j+1][a|(dig[m-i+1]==1)][fun(b,1,dig[i])][c]+=l+k*(2-c);
                        if(a|(dig[m-i+1]==1)){
                            //1,0
                            f[i][j+1][a][fun(b,0,dig[i])][c]+=k;
                            s[i][j+1][a][fun(b,0,dig[i])][c]+=l+k*(2-c);
                            //1,1
                            f[i][0][a][fun(b,1,dig[i])][1]+=k;
                            s[i][0][a][fun(b,1,dig[i])][1]+=l+k*(2-j);
                        }
                    }
                }
            }
        }
    }
    if(m&1){
        int i=m+1>>1;
        for(int j=0;j<=m;j++){
            for(int a=0;a<2;a++){
                for(int b=0;b<3;b++){
                    for(int c=0;c<2;c++){
                        ll k=f[i-1][j][a][b][c];
                        ll l=s[i-1][j][a][b][c];
                        if(!k) continue;
                        //0
                        f[i][0][a|(dig[m-i+1]==1)][b][0]+=k;
                        s[i][0][a|(dig[m-i+1]==1)][b][0]+=l;
                        //1
                        if(a|(dig[m-i+1]==1)){
                            f[i][0][a][b][0]+=k;
                            s[i][0][a][b][0]+=l+k*(1-j);
                        }
                    }
                }
            }
        }
    }
    int i=m+1>>1;
    for(int j=0;j<=m;j++){
        for(int a=0;a<2;a++){
            for(int b=0;b<3;b++){
                for(int c=0;c<2;c++){
                    if(!a&&!b) continue;
                    ll k=f[i][j][a][b][c];
                    ll l=s[i][j][a][b][c];
                    if(!k) continue;
                    ans+=l;
                    if(c) ans-=k*j;
                }
            }
        }
    }    
}
int main(){
    cin>>n;
    for(ll t=n;t;t>>=1) dig[++m]=t&1;
    for(solve();--m;){
        for(int i=1;i<=m;i++) dig[i]=1;
        solve();
    }
    cout<<ans;
    return 0;
}

 

以上是关于hihoCoder挑战赛28题目2 : 二进制翻转的主要内容,如果未能解决你的问题,请参考以下文章

hihoCoder挑战赛28题目3 : 树的方差

hihoCoder编程练习赛70

hihoCoder挑战赛1 毁灭者问题

HihoCoder1677 : 翻转字符串(Splay)(区间翻转)

hihoCoder 1048 : 状态压缩·二

HihoCoder1873(二进制拆分)