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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/V5ZSQ/article/details/83583810

Description

给定长度为 n n 的非负整数序列 a a ,问有多少个长度为 n n 的非负整数序列 b b

满足:

b i a i b_i\le a_i

b 1   x o r   b 2   x o r   . . .   x o r   b n = a 1   x o r   a 2   x o r   . . .   x o r   a n b_1\ xor\ b_2\ xor\ ...\ xor\ b_n=a_1\ xor\ a_2\ xor\ ...\ xor\ a_n

答案对 1000000009 1000000009 取模

Input

第一行一个正整数 n n

第二行 n n 个非负整数 a i a_i

( 1 n 1 0 5 , a i 2 30 ) (1\le n\le 10^5,a_i\le 2^{30})

Output

输出一个数字,表示答案

Sample Input

4
1 2 3 4

Sample Output

6

Solution

显然 b b 序列和 a a 序列相等为一种合法方案,对于其余方案,枚举使得某个 b i < a i b_i<a_i 的最高位 l l ,即 a , b a,b 序列在比第 l l 位更高的位上均相同,而 b i b_i 在第 l l 位是 0 0 a i a_i 在第 l l 位是 1 1 ,显然 b i b_i 可以在前 l l 位随意求值(均可保证 b i < a i b_i<a_i )使得前 l l 位均满足异或和相等的限制,而其余 n 1 n-1 个数字在前 l l 位的取值只需保证不超过 a a 序列即可,那么问题转化为求 n n 个数字在第 l l 位取值使得 b b 序列不超过 a a 序列且至少存在一个位置使得 b i < a i b_i<a_i 的方案数

d p [ i ] [ 1 / 0 ] [ 1 / 0 ] dp[i][1/0][1/0] 表示前 i i 个数字已经确定,他们在第 l l 位的异或和为 1 / 0 1/0 ,且前 i i 个数字中存在/不存在一个使得 b i < a i b_i<a_i 的位置的方案数,考虑转移

一.若 a i a_i 在第 l l 位是 1 1 ,那么 b i b_i 在第 l l 位有两种选择

1. b i b_i 在第 l l 位取 0 0 ,假设 a i a_i l l 位的取值为 x x ,那么只要 b i b_i 在前 l l 位的取值不超过 a i a_i 即可,有 x + 1 x+1 种方案,此时有转移
d p [ i ] [ j ] [ k ] + = d p [ i 1 ] [ 1 j ] [ k ] ( x + 1 ) dp[i][j][k]+=dp[i-1][1-j][k]\cdot (x+1)
2. b i b_i 在第 l l 位取 0 0 ,那么前 l l b i b_i 可以随意取,此时有转移
d p [ i ] [ j ] [ 1 ] + = d p [ i 1 ] [ j ] [ k ] 2 l dp[i][j][1]+=dp[i-1][j][k]\cdot 2^l
二.若 a i a_i 在第 l l 位是 0 0 ,那么 b i b_i 在第 l l 位只能取 0 0 ,且较低位不能超过 a i a_i ,此时有转移
d p [ i ] [ j ] [ k ] + = d p [ i 1 ] [ j ] [ k ] ( x + 1 ) dp[i][j][k]+=dp[i-1][j][k]\cdot (x+1)
那么对于当前考虑的位 l l ,对答案的贡献即为 d p [ n ] [ r e s ] [ 1 ] 2 l \frac{dp[n][res][1]}{2^l} ,其中 r e s res 表示 a i a_i 在第 l l 位的异或和,除以 2 l 2^l 的原因是需要有一个严格小于 a i a_i b i b_i ,在其他 n 1 n-1 个数字在前 l l 位取值固定之后, b i b_i 的前 l l 位需要做对应的取值使得前 l l 位的异或和也和 a a 序列相同

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;
}

猜你喜欢

转载自blog.csdn.net/V5ZSQ/article/details/83583810