位运算相关

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

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 235 235 2 3 5 235 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就很快…

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6