Newcoder 18 B.Xor(位運算+dp)
阿新 • • 發佈:2018-12-18
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;
}