牛客練習賽60 A-大吉大利[簽到][位運算]

牛客練習賽60 A-大吉大利

題目鏈接(Click)
時間限制:C/C++ 1秒,其他語言2秒
空間限制:C/C++ 262144K,其他語言524288K
64bit IO Format: %lld

題目描述

給定n個整數,依次爲a1,a2,…,an
i=1nj=1n(ai&aj) \sum_{i = 1}^n\sum_{j = 1}^n(a_i\&a_j)
'&'是二進制與運算符。

輸入描述

第一行一個整數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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章