1. 程式人生 > 其它 >Codeforces Good Bye 2020 - E. Apollo versus Pan (位運算 + 數學)

Codeforces Good Bye 2020 - E. Apollo versus Pan (位運算 + 數學)

連結: E. Apollo versus Pan

題意:
給一個長度為 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; }