位运算相关

文章介绍了两个关于位操作的编程问题。第一个问题是找到满足特定与运算条件的最小数字m,通过分析二进制位特性给出解决方案。第二个问题涉及或运算,讨论如何确定是否存在两个子序列,它们的或运算结果相等,提出了统计每位出现次数的方法来判断。
摘要由CSDN通过智能技术生成

1.与运算
(点我)
这个题的大概意思:给 2 2 2个数 n n n x x x,其中满足 n & ( n + 1 ) & ( n + 2 ) & ( n + 3 ) . . . & m = x n\&(n+1)\&(n+2)\&(n+3)...\&m=x n&(n+1)&(n+2)&(n+3)...&m=x,求最小的 m m m,只要满足 m > = x m>=x m>=x即可
这个题在二进制的角度来看就是假设 n n n的位是

100101

那么 x x x一定是从某一位开始与 n n n不同,并且不同的这一位(记为 q q q位)一定是 x x x 0 0 0 n n n 1 1 1;并且从这一位往右 x x x的位都是 0 0 0.只有这样才是满足条件的,假设不是这样。

  • 情况一: x x x q q q位是为 1 1 1 n n n的为 0 0 0:一定不可能。与运算不可能弄出来一个 1 1 1
  • 情况2: x x x q q q位往右还有 1 1 1:一定不可能。既然 x x x是累积与运算的结果,那么与到 q q q位一定是用一个   \space   q + 1 q+1 q+1位(向左数1位)为 1 1 1后面都是 0 0 0的数   \space   与出来的。

综上所述, x x x的位可以是:

100100

在这里有一个 t r i c k trick trick n n n q + 1 q+1 q+1位为 1 1 1.这个 q + 1 q+1 q+1位(向左数1位)为 1 1 1后面都是 0 0 0的数 应该与 n n n q + 1 q+1 q+1位(含)相加(造出 m m m),但是如果 n n n q + 1 q+1 q+1位为 1 1 1,那么 q + 1 q+1 q+1位就会被消掉了。比如说 n = 3 n=3 n=3 x = 2 x=2 x=2最后求出来 m m m 4 4 4,结果是错的。证明略去。
代码:

#include<iostream>
#include<cstdio>
#include<bitset>
typedef long long ll;
using namespace std;
typedef long long ll;
int main(void)
{
	int t;
	scanf_s("%d", &t);
	for (int i = 0; i < t; i++)
	{
		ll n, x;
		scanf_s("%lld%lld", &n, &x);
		bitset<100> n2;
		bitset<100> x2;
		n2 = n;
		x2 = x;
		int flag = 1;
		for (int i = 99; i >= 0; i--)
		{
			 
			if (x2[i] != n2[i]&&x2[i]==1&&n2[i]==0)
			{
				flag = 0;
				break;
			}
			else if (x2[i] != n2[i])
			{
				if (x2[i + 1] == 1)
				{
					flag = 0;
					break;
				}
				for (int j = i - 1; j >= 0; j--)
				{
					if (x2[j] != 0)
					{
						flag = 0;
						break;
					}
				}
				if (flag == 0)
					break;
				x2[i + 1] = 1;
				break;
			}
		}
		if (flag == 0)
			printf("-1\n");
		else
		{
			ll sum = 0;
			for (int i = 99; i >= 0; i--)
			{
				sum = sum * 2 + x2[i];
			}
			printf("%lld\n", sum);
		}
	     	
	}
}

2.或运算
集合?(点我)
这个题的大概意思是给 n n n个数,然后每个数是以位的形式给出的,比如说第 i i i个数有 3 3 3位: 2 , 3 , 5 2,3,5 235 2 , 3 , 5 2,3,5 235位上是 1 1 1)那么第 i i i行的样例就会给出:

3 2 3 5

现在要找出两个子序列,让他们的或运算的结果相等。
我一开始想到集合上去了,就是把每个数的位加入到集合里面,然后看集合大小有没有变化,这个和加入顺序有很大关系,并且在某种程度是错误的:
比如说:

3 1 3 4
3 2 3 1
3 3 5 2

这个样例其实是正确的,但是每次加入集合都有新元素,所以算法不对。实际上只要记录下来所有数的位的出现次数,然后再看是否存在一个数的所有位出现次数 > 1 >1 >1即可。
数据结构:我之前交了个代码超时了,不是很理解 (用的 v e c t o r vector vector,所以统计次数的那个位置使用了 m a p map map

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<map>
using namespace std;
//const int length = 2e5 + 5;
int main(void)
{
	int t;
	scanf_s("%d", &t);
	for (int i = 0; i < t; i++)
	{
		int n;
		scanf_s("%d", &n);
		vector<vector<int>> edge(n);
	//	vector<int> c(length, 0);
		map<int, int> c;
		for (int i = 0; i < n; i++)
		{
			int k;
			scanf_s("%d", &k);
			for (int j = 0; j< k; j++)
			{
				int a;
				scanf_s("%d", &a);
				edge[i].push_back(a);
				c[a]++;
			}
		}
		int flag = 0;
		for (int i = 0; i < n; i++)
		{
			int yh = 1;
			//for (int j = 0; j < edge[i].size(); j++)
			for(int j:edge[i])
			{
				if (c[j] == 1)
				{
					yh = 0;
					break;
				}
			}
			if (yh == 1)
			{
				flag = 1;
				break;
			}
		}
		if (flag)
			printf("YES\n");
		else
			printf("NO\n");
	}
}

![在这里插入图片描述](https://img-blog.csdnimg.cn/386d29b12bdb45f78ee52be52cfe1c9c.png

比较:
1840 m s 1840ms 1840ms是使用局部数组,在循环里用 m e m s e t memset memset
1855 m s 1855ms 1855ms那个用的全局数组,然后在循环里用 m e m s e t memset memset
超时的是把二维数组移到全局,然后在循环里声明一个 v e c t o r vector vector
93 m s 93ms 93ms是在循环里用 m a p map map
可能是开一个 v e c t o r vector vector比较花时间,本来就是险过,但是用 m a p map map就很快…

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值