补这道题的时候差点死在这上面了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;
}