D题: OR
原题链接:https://ac.nowcoder.com/acm/contest/11259/D
题目大意
给定两个长度为 n − 1 n-1 n−1 的非负整数序列 b = ( b 2 , b 3 , . . . , b n ) , c = ( c 2 , c 3 , . . . , c n ) b=(b_2,b_3,...,b_n),c=(c_2,c_3,...,c_n) b=(b2,b3,...,bn),c=(c2,c3,...,cn) 。求满足以下条件的非负整数序列 a a a 的数量:
- ∀ i ∈ [ 2 , n ] , b i = a i − 1 ∣ a i , c i = a i − 1 + a i \forall i\in [2,n],b_i=a_{i-1}|a_i,c_i=a_{i-1}+a_i ∀i∈[2,n],bi=ai−1∣ai,ci=ai−1+ai 。
题解
首先明确一点:
a
+
b
=
a
∣
b
+
a
&
b
a+b=a|b+a\&b
a+b=a∣b+a&b
证明见下表(正确性可以参考进位进行理解):
a i a_i ai( a a a二进制下第 i i i位) | b i b_i bi( b b b二进制下第 i i i位) | a + b a+b a+b | a a a| b b b | a & b a\&b a&b | a a a| b b b+ a & b a\&b a&b |
---|---|---|---|---|---|
1 | 1 | 1+1 | 1 | 1 | 1+1 |
1 | 0 | 1+0 | 1 | 0 | 1+0 |
0 | 1 | 1+0 | 1 | 0 | 1+0 |
0 | 0 | 0+0 | 0 | 0 | 0+0 |
所以题目给定了
a
i
−
1
∣
a
i
a_{i-1}|a_i
ai−1∣ai 和
a
i
−
1
+
a
i
a_{i-1}+a_i
ai−1+ai 相当于给出了
a
i
−
1
∣
a
i
a_{i-1}|a_i
ai−1∣ai 和
a
i
−
1
&
a
i
a_{i-1}\&a_i
ai−1&ai 。
显然,当我们确定了
a
1
a_1
a1 之后,整个
a
a
a 数组都可以被确定,即
a
a
a 数组的元素都是互相唯一制约的
我们可以对每一个二进制位考虑情况数,我们采用动态规划,设
d
p
i
,
j
,
t
dp_{i,j,t}
dpi,j,t 表示
a
a
a 数组前
j
j
j 个数的第
i
+
1
i+1
i+1 个二进制位(即
2
i
2^i
2i )的方案数,根据
&
\&
& 和
∣
|
∣ 性质易得转移式(详见代码),最终答案将所有位的情况数根据乘法原理相乘即可得到,即:
∏
j
=
1
30
∑
i
=
0
1
d
p
n
,
j
,
t
\prod\limits^{30}_{j=1}\sum\limits ^1_{i=0}dp_{n,j,t}
j=1∏30i=0∑1dpn,j,t
参考代码
#include<bits/stdc++.h>
#define ll long long
#define For(i,n,m) for(int i=n;i<=m;i++)
#define FOR(i,n,m) for(int i=n;i>=m;i--)
using namespace std;
void read(int &x){
int ret=0;
char c=getchar(),last=' ';
while(!isdigit(c))last=c,c=getchar();
while(isdigit(c))ret=ret*10+c-'0',c=getchar();
x=last=='-'?-ret:ret;
}
const int MAXN=1e5+5;
int n,b[MAXN],c[MAXN],dp[32][MAXN][2],ans=1;
bool f=false;
int main()
{
read(n);
For(i,2,n)read(b[i]);
For(i,2,n)read(c[i]),c[i]-=b[i];//c数组储存&的值
For(i,0,30){
For(j,0,n)dp[i][j][1]=dp[i][j][0]=0;//初始化为0
dp[i][1][0]=dp[i][1][1]=1;//第一个数字可以任选(若非法也会在操作中被剔除)
For(j,2,n){
if(b[j]&(1<<i)){
if(c[j]&(1<<i))dp[i][j][1]+=dp[i][j-1][1];//&和|都成立,则a[j]与a[j-1]的该二进制位都为1,转移1->1
else dp[i][j][1]+=dp[i][j-1][0],dp[i][j][0]+=dp[i][j-1][1];//&不成立,|成立,则a[j]与a[j-1]的该二进制位恰好一1一0,转移1->0,0->1
}
else{
if(c[j]&(1<<i))f=true;//&成立,|不成立,非法不存在,标记无解
else dp[i][j][0]+=dp[i][j-1][0];//&不成立,|不成立,则a[j]与a[j-1]的该二进制位都为0,转移0->0
}
}
}
if(f)puts("0");//若标记无解输出0
else{
For(i,0,30)ans*=dp[i][n][0]+dp[i][n][1];//计算方案数
printf("%d\n",ans);
}
return 0;
}