题目链接:https://syzoj.com/problem/490
内存限制:512 MiB 时间限制:1000 ms

题目描述

若将一个正整数转换为二进制数,在此二进制数中,我们将数字1的个数多于数字0的个数的这类二进制数称为A类数,否则就称其为B类数。

例如

    (1)10=(1)2

   其中1的个数为1,0的个数为0,则称此数为A类数;

    (13)10=(1101)2

   其中1的个数为3,0的个数为1,则称此数为A类数;

    (10)10=(1010)2

   其中1的个数为2,0的个数也为2,称此数为B类数;

    (24)10=(11000)2

   其中1的个数为2,0的个数为3,则称此数为B类数;

编程输出1~n之中(包括1和n),A、B两类数的个数。

输入格式

输入文件一行,为一个正整数n(1≤n≤20,000,000)

输出格式

输出文件一行,为空格隔开的两个整数a和b,其中a表示A类数的个数,b表示B类数的个数;

样例输入

3

样例输出

2 1

数据范围与提示

对于50%的数据,满足n≤1000
对于100%的数据,满足n≤20,000,000

解题思路

用f[i]来记录i中1的个数,因为i&(i-1)可以消去i最右边的1,故i中1的个数正好比i&(i-1)多1个。用w来记录i的位数,因为只有到2的整数幂的时候i的位数才会发生改变,所以判断一下i是否为2的整数幂,是的话位数加1。

#include <stdio.h>
int f[20000005];
int main() {
    int n, w = 0, ans1 = 0, ans2 = 0;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        f[i] = f[i & (i - 1)] + 1;//i&(i-1)可以消去i最右边的1,故i中1的个数正好比i&(i-1)多一个 
        if (!(i & (i - 1)))       //判断是否为2的整数幂 
            w++;                  //位数加1
        if ((f[i] << 1) > w)      //i的1的个数大于位数w的一半
            ans1++;
        else ans2++;
    }
    printf("%d %d\n", ans1, ans2);
    return 0;
}