Newcoder 18 B.Xor(位运算+dp)

探讨了在特定异或约束下,计算满足条件的非负整数序列数量的算法。通过对序列元素进行位操作分析,利用动态规划解决复杂组合计数问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

给定长度为nnn的非负整数序列aaa,问有多少个长度为nnn的非负整数序列bbb

满足:

bi≤aib_i\le a_ibiai

b1 xor b2 xor ... xor bn=a1 xor a2 xor ... xor anb_1\ xor\ b_2\ xor\ ...\ xor\ b_n=a_1\ xor\ a_2\ xor\ ...\ xor\ a_nb1 xor b2 xor ... xor bn=a1 xor a2 xor ... xor an

答案对100000000910000000091000000009取模

Input

第一行一个正整数nnn

第二行nnn个非负整数aia_iai

(1≤n≤105,ai≤230)(1\le n\le 10^5,a_i\le 2^{30})(1n105,ai230)

Output

输出一个数字,表示答案

Sample Input

4
1 2 3 4

Sample Output

6

Solution

显然bbb序列和aaa序列相等为一种合法方案,对于其余方案,枚举使得某个bi&lt;aib_i&lt;a_ibi<ai的最高位lll,即a,ba,ba,b序列在比第lll位更高的位上均相同,而bib_ibi在第lll位是000aia_iai在第lll位是111,显然bib_ibi可以在前lll位随意求值(均可保证bi&lt;aib_i&lt;a_ibi<ai)使得前lll位均满足异或和相等的限制,而其余n−1n-1n1个数字在前lll位的取值只需保证不超过aaa序列即可,那么问题转化为求nnn个数字在第lll位取值使得bbb序列不超过aaa序列且至少存在一个位置使得bi&lt;aib_i&lt;a_ibi<ai的方案数

dp[i][1/0][1/0]dp[i][1/0][1/0]dp[i][1/0][1/0]表示前iii个数字已经确定,他们在第lll位的异或和为1/01/01/0,且前iii个数字中存在/不存在一个使得bi&lt;aib_i&lt;a_ibi<ai的位置的方案数,考虑转移

一.若aia_iai在第lll位是111,那么bib_ibi在第lll位有两种选择

1.bib_ibi在第lll位取000,假设aia_iailll位的取值为xxx,那么只要bib_ibi在前lll位的取值不超过aia_iai即可,有x+1x+1x+1种方案,此时有转移
dp[i][j][k]+=dp[i−1][1−j][k]⋅(x+1) dp[i][j][k]+=dp[i-1][1-j][k]\cdot (x+1) dp[i][j][k]+=dp[i1][1j][k](x+1)
2.bib_ibi在第lll位取000,那么前lllbib_ibi可以随意取,此时有转移
dp[i][j][1]+=dp[i−1][j][k]⋅2l dp[i][j][1]+=dp[i-1][j][k]\cdot 2^l dp[i][j][1]+=dp[i1][j][k]2l
二.若aia_iai在第lll位是000,那么bib_ibi在第lll位只能取000,且较低位不能超过aia_iai,此时有转移
dp[i][j][k]+=dp[i−1][j][k]⋅(x+1) dp[i][j][k]+=dp[i-1][j][k]\cdot (x+1) dp[i][j][k]+=dp[i1][j][k](x+1)
那么对于当前考虑的位lll,对答案的贡献即为dp[n][res][1]2l\frac{dp[n][res][1]}{2^l}2ldp[n][res][1],其中resresres表示aia_iai在第lll位的异或和,除以2l2^l2l的原因是需要有一个严格小于aia_iaibib_ibi,在其他n−1n-1n1个数字在前lll位取值固定之后,bib_ibi的前lll位需要做对应的取值使得前lll位的异或和也和aaa序列相同

Code

 #include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=100005;
#define mod 1000000009
int add(int x,int y)
{
	x+=y;
	if(x>=mod)x-=mod;
	return x; 
}
int mul(int x,int y)
{
	ll z=1ll*x*y;
	return z-z/mod*mod;
}
int Pow(int x,int y)
{
	ll z=1;
	while(y)
	{
		if(y&1)z=mul(z,x);
		x=mul(x,x);
		y>>=1;
	}
	return z;
}
int n,a[maxn],dp[maxn][2][2];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	int ans=1;
	for(int l=30;l>=0;l--)
	{
		memset(dp,0,sizeof(dp));
		dp[0][0][0]=1;
		int res=0;
		for(int i=1;i<=n;i++)
			if((a[i]>>l)&1)
			{
				res^=1;
				a[i]-=(1<<l);
				for(int j=0;j<=1;j++)
					for(int k=0;k<=1;k++)
					{
						dp[i][j^1][k]=add(dp[i][j^1][k],mul(dp[i-1][j][k],a[i]+1));
						dp[i][j][1]=add(dp[i][j][1],mul(dp[i-1][j][k],1<<l));
					}
			}
			else
			{
				for(int j=0;j<=1;j++)
					for(int k=0;k<=1;k++)
						dp[i][j][k]=add(dp[i][j][k],mul(dp[i-1][j][k],a[i]+1));
			}
		ans=add(ans,mul(dp[n][res][1],Pow(1<<l,mod-2)));
	}
	printf("%d\n",ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值