Codeforces Good Bye 2020 - E. Apollo versus Pan (位運算 + 數學)
阿新 • • 發佈:2021-01-10
題意:
給一個長度為 n(n <= 5e5) 的陣列 求 :
思路:
寫了幾項 發現這個式子可以變成 (x1 & x1 + x1 & x2 + x1 & x3 + …) * (x1 | x1 + x1 | x2 + x1 | x3 + …)+ (x2 & x1 + x2 & x2 + x2 & x3 + …) * (x2 | x1 + x2 | x2 + x2 | x3 + …) + …也就是 n 項和的形式。所以我們只需要快速算出 每一項的值,這裡我們可以按位算貢獻 可以做到 o(60) 的複雜度 ,最後的複雜度就是 o(n * 60),考慮每一個二進位制位,統計每一位 1 的個數,根據位運算的性質就可以求出答案(具體看程式碼)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int maxn = 1e6 + 7;
int n,m;
int T;
ll poww[maxn],a[maxn],cnt[100];
ll ans;
int main(){
scanf("%d",&T);
poww[0] = 1;
for(int i = 1; i <= 1e6; i ++){
poww[i] = poww[i - 1] * 2 % mod;
}
while(T--){
scanf("%d",&n);
memset(cnt,0,sizeof(cnt));
ans = 0;
for(int i = 1; i <= n; i ++){
scanf("%lld",&a[i]);
}
for(int i = 0; i <= 60; i ++){
for(int j = 1; j <= n; j ++) {
if((a[j] >> i) & 1) cnt[i]++;
}
}
for(int i = 1; i <= n; i ++){
ll sum1 = 0,sum2 = 0;
for(int j = 0; j <= 60; j ++){
if((a[i] >> j) & 1){
sum1 = (sum1 + cnt[j] * poww[j]) % mod;
sum2 = (sum2 + n * poww[j]) % mod;
}
else sum2 = (sum2 + cnt[j] * poww[j]) % mod;
}
ans = (ans + sum1 * sum2 % mod) % mod;
}
printf ("%lld\n",ans);
}
return 0;
}