SPOJ-1182 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;

}

你可能感兴趣的:(sort)