牛客-A | B

6 篇文章 0 订阅
3 篇文章 0 订阅

A | B

题目描述

给定两个正整数a,x,统计满足以下条件的b的个数:
1.1≤b≤x

2. a∣b=a+b

输入描述

第一行给出一个 t, 1≤t≤105
接下来 t 行每行一对正整数a,x,1≤a,x≤109

输出描述

输出 t 行,每行一个正整数

Sample Input

2
1 2
2 3

Sample Output

1
1

思路

首先对题意进行理解。首先将数字a展开二进制,这样a中会有很多的0,那么你尽可能的用一个数去填补这个0,使之成为1,且这个数不会大于x。
很容易想到,当a二进制位数比x的二进制位数大的时候,大于x位的地方无论是填0或1都不满足条件,所以我门就可以缩小范围到x的二进制长度去探讨该问题。

		cin >> num >> x;
		while (num)
		{
			A[cnt1++] = num % 2;
			num >>= 1;
		}
		while (x)
		{
			B[cnt2++] = x % 2;
			x >>= 1;
		}
		len = cnt2;
		reverse(A, A + len);
		reverse(B, B + len);

以 275和 238举例子

100010011
11101110

首先对齐
1 0 0 0 1 0 0 1 1
0 1 1 1 0 1 1 1 0

将多余的部分删除
0 0 0 1 0 0 1 1
1 1 1 0 1 1 1 0
我们会发现在其中对应的情况有可能会有00,01,10,11
11的情况
很容易发现,当你当前选择的数比x小的时候,那么后面的数字无论如何选择都是成立的,如果想要合法,那么久要看你选择的后面有多少个0。
例如
1000
1010
你的第一位只能选择0,若选择1则构成的数一定不满足a|b=a+b
那么你构成的数的最大值也就是111也比1010小,所以可以放心构造。
那么就让这个三个位置可填可不填的种类,一共有23=8种,但是你不可以填写0,那么你就需要减去一种故有7种排法。
那么对于任何一组,都可以先忽略第一位的数字,去看剩下位置。
这样我们可以简化问题。

  1. 当第一位只能是0的时候,那么答案就是剩下空余位置的个数直接相关,ans=(1<<tot)-1
  2. 若第一位可以是1的时候,我们可以将第一位先不操作,去看剩下的,再去考虑第一位选择1的情况。

现在的核心问题是如何去处理剩下的。
现在就要对01这种情况,进行研究。
01这种情况更像是一个岔路口,即你可以选择0也可以选择1,显然当你选择0的时候,这种情况下可能会出现的情况就可以直接算得了,(ans=(1<<tot)-1)。当我们选择1的时候我们就要继续往下走,接着我们有可能又遇到选择的情况。整体感觉就像如图所示。
在这里插入图片描述
但是有一种情况是不可以忽略的,就是直走的路是无法填写的,只能是000这种可以满足,但这种情况是可以的,因为最高位是1,哪怕后面全是0也没有问题。
再接下来就是去判断“岔路口”的个数,以及后面有没有不可以处理的情况。

为了方便表示,我们用tot[002],tot[012],tot[102],tot[112],即tot[0]~tot[3]去记录不同的情况的个数,在没有岔路口的时候,我们可以通过这些个数的情况去判别是否走到了头,让答案需要加一(均为0)。

AC代码

#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
int main()
{
	int T;
	cin >> T;
	while (T--)
	{
		int A[64] = { 0 }, B[64] = { 0 }, len;
		ll num, x, ans = 0, cnt1 = 0, cnt2 = 0;
		int tot[4] = { 0 };
		cin >> num >> x;
		while (num)
		{
			A[cnt1++] = num % 2;
			num >>= 1;
		}
		while (x)
		{
			B[cnt2++] = x % 2;
			x >>= 1;
		}
		len = cnt2;
		reverse(A, A + len);
		reverse(B, B + len);

		for (int i = 1; i < len; i++)
			if (!A[i] && !B[i])tot[0]++;
			else if (!A[i] && B[i])tot[1]++;
			else if (A[i] && !B[i])tot[2]++;
			else tot[3]++;
		ans = (1ll << (tot[0] + tot[1])) - 1;
		if (len == 1)
		{
			if (A[0] == 0 && B[0] == 1)
				cout << 1 << endl;
			else cout << 0 << endl;
		}
		else
		{
			if (!A[0])
				for (int i = 1; i < len; i++)
				{
					if (tot[1] == 0 && !tot[3])
					{
						ans++;
						break;
					}
					if (A[i] && B[i])
					{
						A[3]--;
						ans += (1ll << (tot[0] + tot[1]));
						break;
					}
					else if (!A[i] && B[i])
					{
						tot[1]--;
						ans += (1ll << (tot[0] + tot[1]));
						if (tot[1] == 0 && !tot[3])
						{
							ans++;
							break;
						}
					}
					else if (A[i] && !B[i])
						tot[2]--;
					else tot[0]--;
				}
			cout << ans << endl;
		}
	}
}

By-轮月

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
牛客 a卷2022年第四季度的华为题目中,要求考生设计一种高效的数据结构,能够支持以下几种操作: 1. 添加一个元素 2. 删除一个元素 3. 查找是否存在某个元素 4. 返回元素的总数 该数据结构要求满足空间复杂度较小、时间复杂度较低、能够快速地进行查找和修改等多种操作。 想要编写这样一种数据结构,我们可以参考许多已有的经典算法与数据结构,如二叉树、哈希表、红黑树等,通过综合利用它们的优点来实现这个问题的解决。 例如,我们可以通过哈希表来存储所有元素的值,并在每个哈希链表的元素中再使用红黑树来进行排序与查找。这样,我们既能够轻松地进行元素的添加和删除操作,也能够在查找较大数据范围和数量时保持较高的速度与效率。同时,由于使用了多个数据结构来协同完成这个问题,我们也能够在空间复杂度上适度地进行优化。 当然,在具体设计这个数据结构的过程中,我们还需要考虑一些实践中的细节问题,例如如何避免哈希冲突、如何处理数据丢失与被删除元素所占用的空间等问题,这都需要相应的算法与流程来进行处理。 总体来看,设计这种支持多种操作的高效数据结构,需要我们具备丰富的算法知识和编程实践能力,同时需要我们在具体处理问题时能够将多种算法和数据结构进行有效地结合。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Round moon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值