G - Sum of xor sum UVALive - 8518
感谢男朋友的悉心教导!!!
给你n个数,进行Q次询问,每次询问都问你l-r区间内所有数的子集合的异或值之和%1e9+7
首先s[]:存储前缀异或和:s[i]=s[i-1]^a[i],那么a[l]+a[l+1]+……+a[r]=s[r]-s[l-1];
接下来预处理k[i][j][k],存储前i个前缀异或值,二进制位为j是k的个数
t[i]:存储1-i中所有子集合的异或值之和
t[i]=t[i-1]+s[0]^s[i]+s[1]^s[i]+……+s[i-1]^s[i]
那么s[0]^s[i]+s[1]^s[i]+……+s[i-1]^s[i]可以通过前i个数第j位的值与我当前前缀异或值不同的话,那么每个k[i][j][k]可以贡献的值是1<<j;
所以进行询问时,ans=t[r]-t[l-1]-sum
sum是左端点在0-(l-2),右端点在l-r中的所有异或和
所以就应该用sum+=左边所有数j位为1*右边所有数j位为0+左边所有数j位为0*右边所有数j位为1*(1<<j)--->当前位的贡献值
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int mod=1e9+7;
int s[maxn],k[maxn][23][2],t[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,Q;
scanf("%d%d",&n,&Q);
for(int i=0;i<=21;i++)
k[0][i][0]=1;
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
s[i]=s[i-1]^x;
for(int j=21;j>=0;j--)
{
if((s[i]>>j)&1)
{
k[i][j][1]=k[i-1][j][1]+1;
k[i][j][0]=k[i-1][j][0];
}
else
{
k[i][j][0]=k[i-1][j][0]+1;
k[i][j][1]=k[i-1][j][1];
}
}
t[i]=t[i-1];
for(int j=21;j>=0;j--)
{
if((s[i]>>j)&1) t[i]=(t[i]+((1<<j)*k[i][j][0]%mod))%mod;
else t[i]=(t[i]+((1<<j)*k[i][j][1])%mod)%mod;
}
}
while(Q--)
{
int l,r;
scanf("%d%d",&l,&r);
int ans=(t[r]-t[l-1]+mod)%mod;
if(l>=2)
{
for(int j=21; j>=0; j--)
{
int x1=k[r][j][1]-k[l-1][j][1],x2=k[r][j][0]-k[l-1][j][0];
int y1=k[l-2][j][1],y2=k[l-2][j][0];
ans=(mod+ans-((1<<j)*(x1*y2+x2*y1)%mod))%mod;
}
}
printf("%d\n",ans);
}
}
return 0;
}