zoukankan      html  css  js  c++  java
  • SPOJ1182 Sorted bit squence 数位DP

    这题虽然说是什么按位DP,其实尼玛不是组合数学么。不过硬是用模板的按位DP实现了,其实也就是记忆化搜索,本题恶心就在于有负数的存在,其实对付它就是把正数的第33位都变成1,用long long来处理,这样既保证了负数小于正数,又可以化成单一的区间了。在按位统计的时候记得当1出现在33位的时候不统计这个1。这题思路也就是先把[a, b]区间内含有一个1,两个1,三个1...的数的个数全部统计出来,一个for循环就能够找出该数字出现在有几个1的一段上,然后再二分查找答案,最后输出。

    代码如下:

    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    typedef unsigned int Int;
    
    Int a, b;
    
    int bit[35], sum[35], dp[35][35], k; // 如何用记忆化去优化搜索
    // sum[i]表示i个1的数有多少个
    // dp[i][j] 代表剩余i位在没有约束的条件下产生j个1的组合数
    
    long long x, y, ret;
    
    void aly(int pos, int statu, int limit, int sign)
    {
        if (pos == -1) {
            sum[statu] += sign;
            return;
        }
        if (!limit && dp[pos][0] != -1) { // dp[i][0] - dp[i][32]是成套更新的 
            for (int i = 0; i <= 32; ++i) {
                sum[i+statu] += sign * dp[pos][i];
            }
            return;
        }
        int s, end = limit ? bit[pos] : 1;
        if (!limit) {
            for (int i = 0; i <= 32; ++i) {
                dp[pos][i] = sum[i]; // 先记录    
            }
        }
        for (int i = 0; i <= end; ++i) {
            s = statu;
            if (i == 1 && pos != 32) s = statu + 1;  // 只需要对状态更新进行判定,最高位(32位)为虚位,不需要计算 
            aly(pos-1, s, limit && i == end, sign);
        }
        if (!limit) {
            for (int i = 0; i <= 32; ++i) {
                dp[pos][i] = abs(sum[i+statu] - dp[pos][i]);    // 可能带来负值,计算出增加的值 
            }
        }
    }
    
    void Cal(long long z, int sign)
    {
        int idx = -1;
        while (z) {
            bit[++idx] = z % 2;
            z /= 2;    
        }
        aly(idx, 0, 1, sign);
    }
    
    Int Ac(long long z, int loc, int Len)
    {
        int idx = -1;
        memset(sum, 0, sizeof (sum));
        Cal(z, 1), Cal(x-1, -1);
        return sum[Len] >= loc;
    }
    
    long long bsearch(long long l, long long r, int loc, int Len) // 有Len个1的,第loc个数 
    {
        long long mid, ret;
        while (l <= r) {
            mid = (l + r) >> 1;
            if (Ac(mid, loc, Len)) {
                ret = mid;
                r = mid - 1;
            }
            else {
                l = mid + 1;    
            }
        }
        return ret;
    }
    
    int main()
    {
        memset(dp, 0xff, sizeof (dp));
        int T;
        scanf("%d", &T);
        while (T--) {
            memset(sum, 0, sizeof (sum));
            x = y = 0;
            scanf("%u %u %u", &a, &b, &k);
            x |= a, y |= b;
            if (int (b) >= 0) {
                y += 1LL << 32; // 给所有的正数加上一位的虚位 
            }
            if (int (a) >= 0) {
                x += 1LL << 32;
            }
            Cal(y, 1), Cal(x-1, -1);  // 已经统计出了区间内含有各个1的数字的数量
            for (int i = 0; i <= 32; ++i) {
                k -= sum[i];
                if (k <= 0) {
                    k += sum[i]; // 去区间内寻找第k个位数为i的数字
                    ret = bsearch(x, y, k, i);
                    printf("%d\n", int (ret));
                    break;
                }
            }
        }
        return 0;
    }
  • 相关阅读:
    剑指63.数据流中的中位数
    剑指62.二叉搜索树的第k个结点
    JPA ---- EntityManager使用
    JPA ---- EntityManager介绍
    win10多桌面切换
    $emit子组件如何传递多个参数
    height高度自适应
    vue Avoided redundant navigation to current location
    Ant Design 使用小结
    Object.keys方法之详解
  • 原文地址:https://www.cnblogs.com/Lyush/p/2637590.html
Copyright © 2011-2022 走看看