E. Apollo versus Pan 拆位位运算

这篇博客主要探讨了一道涉及位运算和数组处理的数学问题,通过化简公式并采用拆位计算的方法,给出了求解该问题的代码实现。文章详细解释了如何利用位运算符`&`和`|`来处理数组元素,以及如何避免在大整数计算中出现溢出的问题,展示了在算法竞赛和编程实践中的技巧应用。
摘要由CSDN通过智能技术生成

E. Apollo versus Pan

题意

n n n 个数 x i ( 1 ≤ i ≤ n ) x_i(1\le i \le n) xi(1in),求 ∑ i = 1 n ∑ j = 1 n ∑ k = 1 n ( x i & x j ) ⋅ ( x j ∣ x k ) \sum_{i=1}^{n}\sum_{j=1}^{n}\sum_{k=1}^{n}(x_i\&x_j)\cdot(x_j|x_k) i=1nj=1nk=1n(xi&xj)(xjxk)

题解
  • 首先化简式子

= ∑ i = 1 n ∑ j = 1 n ∑ k = 1 n ( x i & x j ) ⋅ ( x j ∣ x k ) = ∑ j = 1 n ∑ i = 1 n ∑ k = 1 n ( x i & x j ) ⋅ ( x j ∣ x k ) = ∑ j = 1 n ( ∑ i = 1 n ( x i & x j ) ) ⋅ ( ∑ k = 1 n ( x j ∣ x k ) ) \begin{aligned} &=\sum_{i=1}^{n}\sum_{j=1}^{n}\sum_{k=1}^{n}(x_i\&x_j)\cdot(x_j|x_k)\\ &=\sum_{j=1}^{n}\sum_{i=1}^{n}\sum_{k=1}^{n}(x_i\&x_j)\cdot(x_j|x_k)\\ &=\sum_{j=1}^{n}(\sum_{i=1}^{n}(x_i\&x_j))\cdot(\sum_{k=1}^{n}(x_j|x_k)) \end{aligned} =i=1nj=1nk=1n(xi&xj)(xjxk)=j=1ni=1nk=1n(xi&xj)(xjxk)=j=1n(i=1n(xi&xj))(k=1n(xjxk))

  • 然后拆位计算就可以了:先统计每一位的和,那么前面与的部分的 n n n 项和就是在那一位 x j x_j xj 1 1 1 的基础上, n n n 个数这一位的和,否则为 0 0 0 ,后面或的部分,如果 x j x_j xj 这一位是 1 1 1 ,那么贡献就是这一位的值乘 n n n , 否则就是 n n n 个数这一位的和。枚举 j j j 累加即可。需要注意 n ⋅ b a s ( b a s = 1 < < i ) n\cdot bas(bas=1<<i) nbas(bas=1<<i) 可能会暴longlong,需要对bas取模。
代码
#pragma region
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; ++i)
#define per(i, a, n) for (int i = n; i >= a; --i)
#pragma endregion
const int maxn = 5e5 + 5;
const ll mod = 1e9 + 7;
ll a[maxn];
int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        int n;
        scanf("%d", &n);
        rep(i, 1, n) scanf("%lld", &a[i]);
        vector<ll> s(65);
        rep(i, 0, 60) rep(j, 1, n) {
            ll bas = 1LL << i;
            if (a[j] & bas) s[i] = (s[i] + bas) % mod;
        }
        ll ans = 0;
        rep(j, 1, n) {
            ll x = 0, y = 0;
            rep(i, 0, 60) {
                ll bas = 1LL << i;
                if (bas & a[j]) x = (x + s[i]) % mod;
            }
            rep(i, 0, 60) {
                ll bas = 1LL << i;
                (y += (bas & a[j]) ? n * (bas % mod) : s[i]) %= mod;
            }
            ans = (ans + x * y) % mod;
        }
        printf("%lld\n", ans);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值