站在巨人的肩膀上写出了这篇题解
有错误也欢迎各路dalao帮指出orz
题目大意
给定两个非负数组,构造出数组
满足:
。求出满足要求的数组
的数量。
思路
直接枚举然后检测的话,会TLE,因为每一位都有两种选择,且由于
数组的存在,导致二进制下的每一位都不是独立的(因为有进位的存在)。比如我们认为答案可能在000~111(二进制下)之间。我们必须从000一直枚举到111。
假如我们可以让每一位都独立的话,就可以单独检验每一位是否可以为0或1。
事实上,加法运算有一个位运算的转化(这个不知道大概就寄了,比如我)
很好验证,那么
=>
=>
令
=>
现在我们有两个数组了,注意
和
的区别:**
数组是通过加法运算来限制
数组的,而
数组是通过“按位与”运算来限制
数组的,是没有进位操作的,所以对于d数组,二进制下每一位是相互独立的**
也就是说,因为每一位是相互独立的,我们可以单独枚举每一位的可能值,然后用乘法原理把结果相乘。
比如我们认为答案可能在000到111之间,那么我们只用枚举第一位是否可以为0,1,第二位是否可为0,1,第三位可否为0,1.然后把结果数量相乘即可。
那么如何验证呢?
对于每一位,都有 。
那么无非是这三种情况:
这一位是
,
这一位是
,那么
这一位必须是1。
这一位是
,
这一位是
,那么
这一位可以为
的前提就是
这一位可以为
,同理,
这一位可以为
的前提就是
这一位可以为
。(它们或起来为1,与起来为0,那么肯定一个1一个0)
这一位是
,
这一位是
,那么显然
均为0。
怎么会有两个数与起来为但是或起来为
呢
具体做法
我们求出数组后,按位枚举,对于每一位枚举
第
位是否可以为0,1然后对于每一个数
进行检验。
检验时,用preBit0表示上一个数的这一位(即的第
位)可否为0,用preBit1表示上一个数的这一位可否为1(0为不可以,1为可以)。
用nowBit0表示这个数的这一位(即的第
位)可否为0,nowBit1表示这个数的这一位可否为1。
然后进行递推即可。
代码
#include <cstdio> #include <iostream> using namespace std; const int maxN = 1e5 + 7; long long b[maxN], c[maxN], d[maxN]; int n; int main() { scanf("%d", &n); for(int i = 2; i <= n; ++i) scanf("%lld", &b[i]); for(int i = 2; i <= n; ++i) scanf("%lld", &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; for(int j = 2; j <= n; ++j) { int nowBit0 = 0, nowBit1 = 0; int x = d[j] >> i & 1, y = b[j] >> i & 1; //第j个数的第i位 if(x == 0 && y == 0) //上一个数能为0,那这个数也能为0,(这一位不能为1了,所以nowBit1就还是0) nowBit0 = preBit0; //如果上一个数不能为0,那么显然就不合法了,那么对答案的贡献就是0了。 if(x == 0 && y == 1) { nowBit0 = preBit1; nowBit1 = preBit0; } if(x == 1 && y == 1) nowBit1 = preBit1;//上一个数能为1,那这个数也能为1,(这一位不能为1了,所以nowBit0就还是0) preBit0 = nowBit0; preBit1 = nowBit1; } ans *= preBit0 + preBit1; } printf("%lld\n",ans); return 0; }