zl程序教程

您现在的位置是:首页 >  其它

当前栏目

【PNR#2 Div1 A】恰钱(数位 DP)

DP 数位
2023-09-27 14:28:28 时间

恰钱

题目链接:PNR#2 Div1 A

题目大意

给你一个区间,要你判断里面是否有数满足二进制 1 个数等于末尾 0 个数,如果有就找出一个。

思路

考虑直接上数位 DP,就用二进制来 DP 即可。
维护是否在碰左边界右边界, 1 1 1 的个数,末尾连续 0 0 0 的个数,以及当前形成的数。

然后分 0 , 1 0,1 0,1 讨论一下,具体看代码。

代码

#include<cstdio>

using namespace std;

int q, l, r;

int slove(int now, bool ld, bool rd, int num1, int lst0, int val) {
	if (now < 0 && val && lst0 == num1) return val;
	if (lst0 + now + 1 < num1) return -1;
	if (!ld && !rd) {
		if (!val) return -1;
		if (((num1 + 1) + 1 <= now + 1) || (num1 == lst0 + now + 1)) {
			if (((num1 + 1) + 1 <= now + 1)) return val | (1 << (num1 + 1));
				else return val;
		}
		return -1;
	}
	for (int i = (ld ? ((l >> now) & 1) : 0); i <= (rd ? ((r >> now) & 1) : 1); i++) {
		if (i == 0) {
			int x = slove(now - 1, ld & (i == ((l >> now) & 1)), rd & (i == ((r >> now) & 1)), num1, lst0 + 1, val);
			if (x != -1) return x;
		}
		else {
			int x = slove(now - 1, ld & (i == ((l >> now) & 1)), rd & (i == ((r >> now) & 1)), num1 + 1, 0, val | (1 << now));
			if (x != -1) return x;
		}
	}
	return -1;
}

int main() {
//	freopen("ex_data3.in", "r", stdin);
//	freopen("write.txt", "w", stdout);
	
	scanf("%d", &q);
	while (q--) {
		scanf("%d %d", &l, &r);
		printf("%d\n", slove(31, 1, 1, 0, 1, 0));
	}
	
	return 0;
}