题解 P3917 【异或序列】
我们定义第x个数中从右往左数第i位的贡献:有多少个在x之前的数j满足aj xor aj+1 xor ... xor ax的第i位是1.
设dp[x][i]表示第x个数第i位的贡献,a[x][i]表示第x个数第i位是多少。
若a[x][i]=0,取j=x不会产生贡献。若j小于x,aj xor aj+1 xor ... xor ax=aj xor aj+1 xor ... xor ax-1。所以dp[x][i]=dp[x-1][i].
若a[x][i]=1,取j=x产生1的贡献。若j小于x,aj xor aj+1 xor ... xor ax和aj xor aj+1 xor ... xor ax-1相反。所以dp[x][i]=(x-1-dp[x-1][i])(j小于x的贡献)+1(j=x的贡献)=x-dp[x-1][i].
设DP[x]=dp[x][0]×1+dp[x][1]×2+dp[x][2]×4+...+dp[x][31]×2^31.
则答案就是DP[1]+DP[2]+...+DP[n].
时间复杂度:O(32n)
空间复杂度:O(32n)
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<math.h>
using namespace std;
int n,a[100005],dp[100005][32];
long long ans;
int main()
{
int i,j;
scanf("%d",&n);
for(i=1;i<=n;++i) scanf("%d",&a[i]);
for(i=1;i<=n;++i)
for(j=31;j>-1;--j)
{
if(a[i]&(1<<j)) dp[i][j]=i-dp[i-1][j];
else dp[i][j]=dp[i-1][j];
ans+=1LL*dp[i][j]*(1<<j);
}
printf("%lld",ans);
return 0;
}
我们发现dp[x][i]的值只能由dp[x-1][i]转移得到,所以可以用滚动数组,边转移边统计答案。
时间复杂度:O(32n)
空间复杂度:O(32)
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<math.h>
using namespace std;
int n,a,dp[32];
long long ans;
int main()
{
int i,j;
scanf("%d",&n);
for(i=1;i<=n;++i)
{
scanf("%d",&a);
for(j=31;j>-1;--j)
{
if(a&(1<<j)) dp[j]=i-dp[j];
ans+=1LL*dp[j]*(1<<j);
}
}
printf("%lld",ans);
return 0;
}