zoukankan      html  css  js  c++  java
  • UVALive 4864 Bit Counting --记忆化搜索 / 数位DP?

    题目链接: 题目链接

    题意:如果一个数二进制n有k位1,那么f1[n] = k,如果k有s位二进制1,那么f2[n] = f1[k] = s.  如此往复,直到fx[n] = 1,此时的x就是n的”K值“,现在要求[L,R]内的”K值“为X的数有多少个。(1<=L<=R<=10^18)

    解法:首先可以看到10^18最多只有61位左右的数,所以我们只需处理1~61之间每个数有多少个1,即可知道1~61之间每个数”K值“是多少。

    然后就将求[L,R]之间的个数变成求[1,R]-[1,L-1],所以我们只需数出对于每个数n,[1,n]之间有多少个数的”K值“为X即可。

    对于二进制来说,可以这样搜索出来:

    比如<=101001,要满足有k个1的数的个数,那么我们从高位往低位扫,扫到第一个1,那么现在有两种情况:

    1.此处放1:那么就等于求<=1001时放k-1个1的数的个数

    2.此处放0:那么后面就随便放了,为C[5][k]

    所以如此递归的搜索就可得出答案,也可以用DP做。

    代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <cmath>
    #include <algorithm>
    #define ll long long
    using namespace std;
    
    int Count(ll state) {
        int cnt = 0;
        while(state) {
            if(state & 1LL) cnt++;
            state >>= 1;
        }
        return cnt;
    }
    int WEI(ll state) {
        int cnt = 0;
        while(state) {
            cnt++;
            state >>= 1;
        }
        return cnt;
    }
    ll C[100][100];
    int in[67];
    
    void init()
    {
        C[0][0] = 1;
        for(int i = 1; i < 90; i++) {
            C[i][0] = 1;
            for(int j = 1; j <= i; j++) {
                C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
            }
        }
        memset(in,0,sizeof(in));
        in[1] = 0;
        for(int i=2;i<=61;i++)
            in[i] = in[Count(i)]+1;
    }
    int X;
    
    ll get(ll state,int cnt) {
        if(state < 0) return 0;
        int len = WEI(state);
        if(len < cnt) return 0;   // not enough
        if(cnt == 0)  return 1;   // no demand
        return get(state-(1LL<<(len-1)),cnt-1) + C[len-1][cnt];
    }
    
    ll getsum(ll R,ll L) {
        ll ans = 0;
        for(int i=1;i<=61;i++)
            if(in[i]+1 == X) ans += get(R,i)-get(L-1,i);
        return ans;
    }
    
    int main()
    {
        init();
        int i,j;
        ll L,R;
        while(scanf("%lld%lld%d",&L,&R,&X)!=EOF && L+R+X)
        {
            ll ans = 0;
            if(X == 0 && L == 1LL) { puts("1"); continue; }
            if(X == 1 && L == 1LL) ans--;  //1's binary code is 1, but 1 is not in (X==1)
            ans += getsum(R,L);
            cout<<ans<<endl;
        }
        return 0;
    }
    View Code
  • 相关阅读:
    vue --- 全局弹窗,只调用对应实例
    代理相关;win操作
    mongoBD + node + express
    菜鸟初学 node 推荐 亲测easy
    H5 ---- 点击遍历所有标签,复制对应的文本
    async与await初步应用
    C# Enum 添加自定义Attribute,然后通过泛型与反射的方式得到事先定义的标记
    VS2013 C# 调用 cognex 的QuickBuild做程序时发生一个错误
    C# 获取数组的内存地址
    利用反射插入数据库与更新数据库
  • 原文地址:https://www.cnblogs.com/whatbeg/p/4237697.html
Copyright © 2011-2022 走看看