给定一个长度为 nn 的数组 a1,a2,...,ana1,a2,...,an。 请你求出下面式子的模1e9+71e9+7的值。
∑i=1n−1∑j=i+1n(aiXORaj)∑i=1n−1∑j=i+1n(aiXORaj)
输入格式
第一行一个数字 nn。
接下来一行 nn 个整数 a1,a2,…,ana1,a2,…,an。
输出格式
一行一个整数表示答案。
样例输入
3
1 2 3
样例输出
6
数据规模
所有数据保证 2≤n≤300000,0≤ai<2602≤n≤300000,0≤ai<260。
思路:在异或时只有0与1异或时才会产生贡献,相同的话就没有贡献,我们可以将位数分开处理,每位分开处理,因为0与1异或不需要考虑它俩谁先出现,所以我们在处理时就不用考虑顺序了,不用管谁先出现,只管出现过即可。现在考虑每位的贡献,为什么要每位考虑呢?因为我们是从前往后遍历的,每个数都有出现的机会,即使这个数(或者位)是0,只要后面有1即可,它们就会产生贡献。每位的贡献是怎样计算的呢?只要知道每位的1的数量,乘上0的个数,因为1会与0进行(排列)组合,每个1都有与0匹配的机会,还是因为0与1不用考虑出现顺序,这就不是产生贡献的组合数吗,然后乘上第i位的大小不是就可以了,也就是第i位的大小。这不就完成了。
两端数组合时,需要用乘法。
完整代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
const int N=100;
int cnt[N];
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n;
cin>>n;
int x;
for(int i=1;i<=n;i++)
{
cin>>x;
int idx=0;
while(x)
{
if(x&1)
{
cnt[idx]++;
}
idx++;
x>>=1;
}
}
int res=0;
for(int i=0;i<=60;i++)
{
res=(res+((1ll<<i)%mod)*(cnt[i]*(n-cnt[i])%mod)%mod)%mod;
}
/*int res=0;
for(int i=0;i<60;i++)
{
res+=((1ll<<i)%mod)*(cnt[i]*(n-cnt[i])%mod)%mod;
if(res>=mod)res-=mod;
}*/
cout<<res<<endl;
return 0;
}