Description
给定长度为 的非负整数序列 ,问有多少个长度为 的非负整数序列 ,
满足:
答案对 取模
Input
第一行一个正整数
第二行 个非负整数
Output
输出一个数字,表示答案
Sample Input
4
1 2 3 4
Sample Output
6
Solution
显然 序列和 序列相等为一种合法方案,对于其余方案,枚举使得某个 的最高位 ,即 序列在比第 位更高的位上均相同,而 在第 位是 而 在第 位是 ,显然 可以在前 位随意求值(均可保证 )使得前 位均满足异或和相等的限制,而其余 个数字在前 位的取值只需保证不超过 序列即可,那么问题转化为求 个数字在第 位取值使得 序列不超过 序列且至少存在一个位置使得 的方案数
以 表示前 个数字已经确定,他们在第 位的异或和为 ,且前 个数字中存在/不存在一个使得 的位置的方案数,考虑转移
一.若 在第 位是 ,那么 在第 位有两种选择
1.
在第
位取
,假设
前
位的取值为
,那么只要
在前
位的取值不超过
即可,有
种方案,此时有转移
2.
在第
位取
,那么前
位
可以随意取,此时有转移
二.若
在第
位是
,那么
在第
位只能取
,且较低位不能超过
,此时有转移
那么对于当前考虑的位
,对答案的贡献即为
,其中
表示
在第
位的异或和,除以
的原因是需要有一个严格小于
的
,在其他
个数字在前
位取值固定之后,
的前
位需要做对应的取值使得前
位的异或和也和
序列相同
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;
}