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; }