牛客练习赛60 A-大吉大利[签到][位运算]

牛客练习赛60 A-大吉大利

题目链接(Click)
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

给定n个整数,依次为a1,a2,…,an
∑ i = 1 n ∑ j = 1 n ( a i & a j ) \sum_{i = 1}^n\sum_{j = 1}^n(a_i\&a_j) i=1nj=1n(ai&aj)
'&'是二进制与运算符。

输入描述

第一行一个整数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
#include
#include
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;
}

你可能感兴趣的:(ACM)