牛客練習賽60 A-大吉大利
題目鏈接(Click)
時間限制:C/C++ 1秒,其他語言2秒
空間限制:C/C++ 262144K,其他語言524288K
64bit IO Format: %lld
題目描述
給定n個整數,依次爲a1,a2,…,an。
求
'&'是二進制與運算符。
輸入描述
第一行一個整數n.
第二行n個整數ai.
輸出描述
一個整數表示上述求和式的答案.
樣例輸入
5
1 2 3 4 5
樣例輸出
33
備註
1≤ n ≤1e5
0≤ ai ≤1e8
分析
先瞄一眼所求和式,明面上O(n2)的時間複雜度。
再看一眼數據範圍:1≤ n ≤1e5,心如死灰。
當然,ACMer早已見怪不怪,縱使題目虐我千百遍,我仍待她如初戀。
咱們先好好觀察一下這“初戀”,和式中心的’&'也許是突破口,衆所周知,位運算往往用來優化時間、空間複雜度,出題人也許在這藏了什麼貓膩。
真·分析
正所謂,好內存不如爛硬盤 ,咳咳,我們先在紙上模擬一下這個和式。
先就 i==1 來看:
a1&a1+a1&a2+a1&a3+…+a1&an
即a1和每一項分別相與(&)再相加(+)
既然&是按位與,那麼我們就從位的角度來看,分析每一位如何變化:
假如a1的某一位是0,那麼無論相與對象的該位是什麼,相與結果皆爲0,對結果無貢獻。
假如a1的某一位是1,同理,只有當相與對象該位爲1時,結果才爲1,貢獻爲1*(該位權重)
不僅是a1,對於每一個ai均是如此,假設特定位爲第k位,所有ai該位上共有m個1
則,該位對答案的貢獻爲:m*m*(1<<(k-1))
如此,只要預處理出每一位上1的個數即可,複雜度O(n)
“Talk is Cheap. Show me the Code.”
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
const int maxn = 1e5 + 5;
typedef long long ll;
int num[maxn];
int b[31];//由於非負,第32位必位0
int main(void)
{
int n;
scanf("%d", &n);
ll ans = 0;
for (int i = 0; i < n; i++)
scanf("%d", &num[i]);
for (int i = 0; i < n; i++) {
int t = num[i], j = 0;
while (t)//二進制轉換,統計1的個數
b[j++] += (t & 1), t >>= 1;
}
for (int i = 0; i < 31; i++)//貢獻彙總
ans += (ll)b[i] * b[i] * (1LL << i);
printf("%lld\n", ans);
return 0;
}