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;
    }
  • 相关阅读:
    tuple 元组及字典dict
    day 49 css属性补充浮动 属性定位 抽屉作业
    day48 选择器(基本、层级 、属性) css属性
    day47 列表 表单 css初识
    day 46 http和html
    day 45索引
    day 44 练习题讲解 多表查询
    day 40 多表查询 子查询
    day39 表之间的关联关系、 补充 表操作总结 where 、group by、
    day38 数据类型 约束条件
  • 原文地址:https://www.cnblogs.com/Lyush/p/2637590.html
Copyright © 2011-2022 走看看