2021牛客暑期多校训练营8 D题: OR

D题: OR

原题链接:https://ac.nowcoder.com/acm/contest/11259/D

题目大意

给定两个长度为 n − 1 n-1 n1 的非负整数序列 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=ai1ai,ci=ai1+ai

题解

首先明确一点: a + b = a ∣ b + a & b a+b=a|b+a\&b a+b=ab+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
111+1111+1
101+0101+0
011+0101+0
000+0000+0

所以题目给定了 a i − 1 ∣ a i a_{i-1}|a_i ai1ai a i − 1 + a i a_{i-1}+a_i ai1+ai 相当于给出了 a i − 1 ∣ a i a_{i-1}|a_i ai1ai a i − 1 & a i a_{i-1}\&a_i ai1&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=130i=01dpn,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;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值