E. Apollo versus Pan
题意
给 n n n 个数 x i ( 1 ≤ i ≤ n ) x_i(1\le i \le n) xi(1≤i≤n),求 ∑ 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=1n∑j=1n∑k=1n(xi&xj)⋅(xj∣xk)。
题解
- 首先化简式子
= ∑ 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=1∑nj=1∑nk=1∑n(xi&xj)⋅(xj∣xk)=j=1∑ni=1∑nk=1∑n(xi&xj)⋅(xj∣xk)=j=1∑n(i=1∑n(xi&xj))⋅(k=1∑n(xj∣xk))
- 然后拆位计算就可以了:先统计每一位的和,那么前面与的部分的 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) n⋅bas(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);
}
}