异或和之和(牛客小白月赛25 技巧位运算)

异或和之和

链接:https://ac.nowcoder.com/acm/contest/5600/J

题目描述
给一个数组,数组内有 n n 个正整数。
求这些数任取3个数异或运算后求和的值。
也就是说,取一共 C n 3 C_{n}^{3} ​个三元组,计算这些三元组内部异或,之后求和。(具体操作可以见样例描述)
由于该值可能过大,输出其对 1000000007 取模的值。
输入描述:
第一行一个正整数 n n
接下来有 n n 个正整数 a i a_i 。( 1 a i 1 0 18 1≤a_i≤10^{18}
输出描述:
任取三个数、三元组内部位异或后求和对取模的值。
示例1
输入
4
3 4 5 6
输出
10
说明
共有4个三元组: 3 4 5 3 4 6 3 5 6 4 5 6 {3,4,5}、{3,4,6}、{3,5,6}、{4,5,6}
3   X O R   4   X O R   5 = 2 3\ XOR\ 4\ XOR\ 5 = 2
3   X O R   4   X O R   6 = 1 3\ XOR\ 4\ XOR\ 6 = 1
3   X O R   5   X O R   6 = 0 3\ XOR\ 5\ XOR\ 6 = 0
4   X O R   5   X O R   6 = 7 4\ XOR\ 5\ XOR\ 6 = 7
相加为10。

思路:
暴力枚举三个数的组合肯定是不行的。
技巧:分别计算每个二进制位。
1e18有64位,统计出每一位的1的个数,对每一位都带权处理,这样就相当于原来的数的大小没变,只不过拆成了好几个数
异或为1只有两种情况:
1   X O R   1   X O R   1 1\ XOR\ 1\ XOR\ 1
1   X O R   0   X O R   0 1\ XOR\ 0\ XOR\ 0
分别处理每一位,累加和,通过组合数学的方法就能得出结果,别忘了给每一位带上权值

对比暴力枚举的方法,枚举出的所有三个数的组合,把这些三个数的组合对每一位拆分,最后也是得到所有的二进制位的三个位的组合,把每一位归为一类处理,这样一想这个技巧是很合理很好用的。

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;
}

猜你喜欢

转载自blog.csdn.net/weixin_44169557/article/details/106201726
今日推荐