牛客多校8OR(位运算 或 和)

传送门


题目:

在这里插入图片描述
示例1
输入

4
7 5 5
7 9 5
输出

2

大致题意:给出b,c数组的2–n位置上的数据,构造a数组使得:
( 2 ≤ i ≤ n ) (2 \leq i \leq n) (2in) b i = a i − 1 or   a i , c i = a i − 1 + a i b_i = a_{i-1} \text{or} \, a_{i} , c_i = a_{i-1} + a_{i} bi=ai1orai,ci=ai1+ai成立
问可以构造几个满足条件的a数组。

样例中的两个a数组分别为 2 , 5 , 4 , 1 2,5,4,1 2,5,4,1 3 , 4 , 5 , 0 3,4,5,0 3,4,5,0

分析:

1.位运算、二进制:

在二进制中有这样一个公式:
a + b = a and   b + a or   b a+b=a \text{and}\,b +a\text{or}\,b a+b=aandb+aorb

由于加法中包含进位,而二进制的and和or运算中不需要考虑进位问题,所以我们可以通过 ( 2 ≤ i ≤ n ) (2 \leq i \leq n) (2in) c i = c i − b i c_{i}=c_{i}-b_{i} ci=cibi的操作使得题目中a数组与c数组的关系转变为 c i = a i − 1 and   a i c_i = a_{i-1} \text{and}\, a_{i} ci=ai1andai
在这里插入图片描述

2. a a a数组的关联:

在我们模拟样例的时候可以发现一旦确定了 a 1 a_1 a1的值, a i a_i ai ( 2 ≤ i ≤ n ) (2 \leq i \leq n) (2in)的值也有唯一性, a i a_i ai ( 2 ≤ i ≤ n ) (2 \leq i \leq n) (2in)只有两种情况:
1.一个定值
2.没有数值可以满足(即a数组构造失败)
通过这个规律我们可以将题目转换为求有多少种满足条件的 a 1 a_1 a1
但该如何去寻找 a 1 a_1 a1的个数呢?
此时我们可以借助二进制:
从低位( 2 0 2^0 20)到高位( 2 30 2^{30} 230)每一位都查找一遍康康此位满足条件的有2种(0和1均可)还是1种(0或1)还是无论取0还是1都没办法满足(此时可以直接得到0的答案。即 a 1 a_1 a1无论如何取值都不能满足题目的要求)

1. b i b_i bi c i c_i ci二进制之间的一些关系:

以下数据都是用二进制形式表示:
在这里插入图片描述
当只看此条件我们以为只看b和c同位置上的1/0就可以判断a在此位置上的取值可能性那就错了
在这里插入图片描述

错代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=1e5+10;
const ll mod=4933;

int b[N],c[N];

int main()
{
	int n;
	cin>>n;
	for(int i=2;i<=n;i++)cin>>b[i];
	for(int i=2;i<=n;i++){
		cin>>c[i];
		c[i]-=b[i];
	}
	int ans=1;
	for(int i=0;i<31;i++){//枚举a[1]的每一位
		int flag=2;//初始为2种情况(0,1)均可
		for(int j=2;j<=n;j++){
			int bn=b[j]>>i&1;//取b[j]第i位
			int cn=c[j]>>i&1;//取c[j]第i位
			if(bn==cn&&flag==2)flag=1;
			else if(bn==0&&cn==1){
				flag=0;
				break;
			}
		}
		ans=ans*flag;
	}
	cout<<ans<<endl;
	
	return 0;
}

错误原因:没有考虑 a i − 1 a_{i-1} ai1 a i a_{i} ai之间的关联

2. a i − 1 a_{i-1} ai1 a i a_{i} ai之间的关联:

即当情况为1时(只能取1或0)的时候考虑少了点:
在这里插入图片描述

解决方法:

当判断 a 1 a_{1} a1在此位置上只有一种取值之后用fron来记录 a i − 1 a_{i-1} ai1的值,在遍历 b j b_j bj c j c_j cj第i位上的值的过程中进行fron的更新就好啦!
ac代码:(写的有点长不过懂就好hh)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=1e5+10;
const ll mod=4933;

int b[N],c[N];

int main()
{
	int n;
	cin>>n;
	for(int i=2;i<=n;i++)cin>>b[i];
	for(int i=2;i<=n;i++){
		cin>>c[i];
		c[i]-=b[i];
	}
	int ans=1;
	for(int i=0;i<31;i++){//枚举a[1]的每一位
		int flag=2,fron=-1;//初始为2种情况(0,1)均可
		for(int j=2;j<=n;j++){
			int bn=b[j]>>i&1;//取b[j]第i位
			int cn=c[j]>>i&1;//取c[j]第i位
			if(bn==cn&&flag==2){
                flag=1;
                if(bn==0)fron=0;//记录此时a[j]取值
                else fron=1;
            }
            else if(bn&&cn==0&&flag==1){
                if(fron==0)fron=1;//更新a[j]取值
                else fron=0;
            }
            else if(bn==cn&&flag==1){
                if((bn==0&&fron==1)||(bn==1&&fron==0)){
                    cout<<"0"<<endl;
                    return 0;
                }
                //更新a[j]取值
                if(bn==0)fron=0;
                else fron=1;
            }
			else if(bn==0&&cn){
				cout<<"0"<<endl;
                return 0;
			}
		}
		ans=ans*flag;
	}
	cout<<ans<<endl;
	
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值