异或和之和
链接:https://ac.nowcoder.com/acm/contest/5600/J
题目描述
给一个数组,数组内有
个正整数。
求这些数任取3个数异或运算后求和的值。
也就是说,取一共
个三元组,计算这些三元组内部异或,之后求和。(具体操作可以见样例描述)
由于该值可能过大,输出其对 1000000007 取模的值。
输入描述:
第一行一个正整数
。
接下来有
个正整数
。(
)
输出描述:
任取三个数、三元组内部位异或后求和对取模的值。
示例1
输入
4
3 4 5 6
输出
10
说明
共有4个三元组:
相加为10。
思路:
暴力枚举三个数的组合肯定是不行的。
技巧:分别计算每个二进制位。
1e18有64位,统计出每一位的1的个数,对每一位都带权处理,这样就相当于原来的数的大小没变,只不过拆成了好几个数
异或为1只有两种情况:
分别处理每一位,累加和,通过组合数学的方法就能得出结果,别忘了给每一位带上权值。
对比暴力枚举的方法,枚举出的所有三个数的组合,把这些三个数的组合对每一位拆分,最后也是得到所有的二进制位的三个位的组合,把每一位归为一类处理,这样一想这个技巧是很合理很好用的。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
const ll mod = 1e9 + 7;
ll n, cnt[64];
ll power(ll a, ll b)
{
ll res = 1;
while (b)
{
if (b & 1)
res = res * a % mod;
b >>= 1, a = a * a % mod;
}
return res;
}
ll inv(ll x) { return power(x, mod - 2); }
ll C(ll x, ll y)
{
if (x < y)
return 0;
ll res = 1;
for (ll i = 0; i < y; i++)
{
res = res * (x - i) % mod;
res = res * inv(i + 1) % mod;
}
return res;
}
int main()
{
cin >> n;
for (ll i = 0; i < n; i++)
{
ll x, t = 0;
cin >> x;
while (x)
{
if (x & 1)
cnt[t]++;
t++;
x >>= 1;
}
}
ll ans = 0;
for (ll i = 0; i < 64; i++)
{
ans += (1LL << i) % mod * (C(cnt[i], 3) + cnt[i] * C(n - cnt[i], 2) % mod) % mod;
ans %= mod;
}
cout << ans << endl;
return 0;
}