2021牛客多校8D OR(思维题)

补这道题的时候差点死在这上面了orz
题目大意:
给一个数字n
给两个长度为(n-1)的数组b[N],c[N],下标从2开始
要求你找到一个长度为n,下标从1开始的数组a[N]
满足:b[i]=a[i] | a[i-1] c[i]=a[i-1]+a[i]
求这样的数组有多少个
2<=n<=1e5
1<=bi<=2^30
1<=ci<=2^31
c[i]=a[i-1]+a[i]可知,当我们确定了a[1]后,整个a数组都被确定了
所以我们只需要确定有多少个a[1]满足条件
前置知识点:a+b=a&b+a|b
经过一段花里胡哨的推理:

b[i]=a[i-1]|a[i]
c[i]=a[i-1]+a[i]
a[i-1]+a[i]=a[i]&a[i-1]+a[i]|a[i-1]
a[i-1]&a[i]=c[i]-b[i]=d[i]

我们设d[i]=c[i]-b[i]
此时d[i]=a[i-1]&a[i]
这里是 & 和 | 的按位性质
| 运算 0|0=0;0|1=1;1|0=1;1|1=1
& 运算 0&0=0;0&1=0;1&0=0;1&1=1
这里拿样例举例子:
b数组:7 5 5
c数组 : 7 9 5
那么构造出的d数组就是 0 4 0
舍弃c数组只看bd

a: x x x x
b: 7 5 5
d: 0 4 0
将数转为二进制数
a: x x x x
b: 111 101 101
d: 000 100 000
上面说过,只要确定了a[1]就能确定整个a数组,我们枚举出a[1]的每一位的取法,再放在b,d数组中验证( & 和| 是按位运算,不会影响到其他位)
设a[1]的二进制数的第一位既能选1,又能选0
b[2]二进制数的第一位为1 d[2]二进制数的第一位为0
设a[2]二进制数的第一位为x,a[1]二进制数的第一位选0,由b[i]=a[i] | a[i-1] d[i]=a[i-1]&a[i]我们可以得出
0 | x =1
0 & x =0
可以解出x=1。
当a[1]的第一位选1,可以得出
1|x=1
1&x=0
可以解出x=0
a[2]的第一位可以同时选0和1
接下来处理b[3]和d[3],b[4]和d[4]
b[3/4]第一位为1,d[3/4]第一位为0,与b[2],d[2]情况一样,我们就不再讨论,最终结果是a[1]的第一位既可以选1也可以选0
接下来处理a[1]二进制数的第二位
首先是b[2]和d[2],与之前情况相同,略过
现在处理b[3]和d[3]
b[3]的二进制第二位是0,d[3]的二进制第二位是0
当a[2]的第二位选择0,设a[3]的第二位选择x
0|x=0
0&x=0
解得x=0
当a[2]的第二位选择1,设a[3]的第二位选择x
1 | x=0
1 & x =0
显然,这是无解的所以a[3]的第二位只能选择0
现在看b[4]和d[4]
b[4]第二位是0,d[4]第二位是0
由于a[3]的第二位只能选择0,设a[4]第二位选择x,得方程:
0 | x =0
0 & x=0
x=0
所以a[4]的第二位也只能选择0
所以a[1]的第二位只有一种选择:0
同理,我们接下来可以处理第三位
a: x x x x
b: 111 101 101
d: 000 100 000
b[2]第三位=1 d[2]第三位=0
与上面相同,略过不讲,目前为止a[2]可以选1和0
b[3]第三位=1 d[3]第三位=1
设a[3]第三位=x,a[2]第三位取1
1 | x=1
1 &x=1
解得x=1
设a[2]第三位取0
0 | x=1
0 & x=1
显然无解
所以a[3]的第三位只能选1
所以a[1]的第三位也只有一种选择
a[1]的第一位有2种选择,第二位有1种选择,第三位有1种选择,所以一共有2x1x1==2种选择
如果在某一位里0和1都不能选那么整个问题的答案就是0,不存在这样的数
下面有两份代码,一份比较精炼,另一份是把所有情况列举出来一一判断
代码1:

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
long long b[N], c[N], d[N];

//b[i]=a[i-1]|a[i]
//c[i]=a[i-1]+a[i]
//a[i-1]+a[i]=a[i]&a[i-1]+a[i]|a[i-1]
//a[i-1]&a[i]=c[i]-b[i]
//a[i-1]+a[i]-a[i-1]|a[i] = d[i]

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];
	for (int i = 2; i <= n; i++) {
		d[i] = c[i] - b[i];//相邻两位 & 的结果
	}

	long long ans = 1;

	for (int i = 0; i < 31; i++) {
		int prebit0 = 1, prebit1 = 1; //初始默认0/1都能取
		for (int j = 2; j <= n; j++) {
			int nowbit0 = 0, nowbit1 = 0;
			int x = b[j] >> i & 1; //代表 | 运算 0|0=0;0|1=1;1|0=1;1|1=1;
			int y = d[j] >> i & 1; //代表 &       0&0=0;0&1=0;1&0=0;1&1=1
			if(x==0&&y==0){//当前位两者都为0,这一位只能选0
				nowbit0=prebit0;//如果上一位能选0,这一位也只能选。
				//上一位选不了0,这一位也选不了0(直接GG)
				nowbit1=0;
			}
			if(x==0&&y==1){//这一位无论选什么都不会成立
				nowbit0=0;nowbit1=0;
			}
			if(x==1&&y==0){//
				nowbit1=prebit0;
				nowbit0=prebit1;
			}
			if(x==1&&y==1){
				nowbit0=pre
			}
			prebit1 = nowbit1;
			prebit0 = nowbit0; //更新
		}
		ans *= (prebit1 + prebit0);
	}


	cout << ans << endl;
}





代码二:

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n;
long long b[N], c[N], d[N];

int main() {
	cin >> n;
	for (int i = 2; i <= n; i++)
		cin >> b[i];
	for (int i = 2; i <= n; i++)
		cin >> c[i];
	for (int i = 2; i <= n; i++)
		d[i] = c[i] - b[i];
	long long ans = 1;

	for (int i = 0; i < 31; i++) {
		int st0 = 1, st1 = 1; //st0=0代表a1的第i位不能取0,st1=1代表a1的第i位可以取1
		for (int j = 2; j <= n; j++) {
			int nowst0 = 0, nowst1 = 0;//判断当前点能否取0或1
			int x = b[j] >> i & 1;//代表 | 运算 0|0=0;0|1=1;1|0=1;1|1=1;
			int y = d[j] >> i & 1;//代表 & 运算 0&0=0;0&1=0;1&0=0;1&1=1
			if (x == 0 && y == 0) { //如果当前|和&都为0
				if (st0 == 0 && st1 == 0) {
					nowst0 = 0, nowst1 = 0;
				} else if (st0 == 0 && st1 == 1) {
					nowst0 = 0, nowst1 = 0;
				} else if (st0 == 1 && st1 == 0) {
					nowst0 = 1, nowst1 = 0;
				} else if (st0 == 1 && st1 == 1) {
					nowst0 = 1, nowst1 = 0;
				}
			} else if (x == 1 && y == 0) { //必定ai-1或者ai中一个为1,一个为0
				if (st0 == 0 && st1 == 0) {
					nowst0 = 0, nowst1 = 0;
				} else if (st0 == 0 && st1 == 1) {
					nowst0 = 1, nowst1 = 0;
				} else if (st0 == 1 && st1 == 0) {
					nowst0 = 0, nowst1 = 1;
				} else if (st0 == 1 && st1 == 1) {
					nowst0 = 1, nowst1 = 1;
				}
			} else if (x == 0 && y == 1) {
				nowst0 = 0, nowst1 = 0; //两者都不行
			} else if (x == 1 && y == 1) {
				if (st0 == 0 && st1 == 0) {
					nowst0 = 0, nowst1 = 0;
				} else if (st0 == 0 && st1 == 1) {
					nowst0 = 0, nowst1 = 1;
				} else if (st0 == 1 && st1 == 0) {
					nowst0 = 0, nowst1 = 0;
				} else if (st0 == 1 && st1 == 1) {
					nowst0 = 0, nowst1 = 1;
				}
			}
			st0 = nowst0, st1 = nowst1;
		}
		ans *= (st1 + st0);
	}
	cout << ans << endl;
}












评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值