码迷,mamicode.com
首页 > 其他好文 > 详细

【位运算】大吉大利

时间:2020-03-28 01:11:51      阅读:69      评论:0      收藏:0      [点我收藏+]

标签:str   coder   返回值   不能   ref   这一   长度   amp   依次   

大吉大利

给定\(n\)个整数,依次为\(a_1,a_2,...,a_n。\)

\(\sum_{i=1}^n\sum_{j=1}^n(a_i\&a_j)\)\(\&\)是二进制的与运算符

容易想到二重循环

for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
		ans+=(a[i]&a[j]);

时间复杂度为\(O(n^2)\),又\(n<=10^5\),超时了。

考虑与运算的性质:是将每个数字转化成二进制数,再一位一位作计算的。只有当两个数都为1时结果才为1,其它情况结果均为0。

以样例为例,5个数字化为二进制数得到

\(00001\)

\(00010\)

\(00011\)

\(00100\)

\(00101\)

对于最右边一列,在\(a[1]\)与其它数(包括它本身)进行与运算时,这一列所得结果之和为3。\(a[3]\)\(a[5]\)时的情况同理。故这一列在求和过程中的总结果为9。即这一列中1的数目的平方。

根据上述规律,继续计算其它列,最终得到的一串数字即所求和的二进制表示。

注意,将所求和的二进制数转化为十进制时,因为返回值可能需要long long储存,所以不能用math.h里的pow函数(返回类型为double),要重新写一个。

$\color{green}{查看代码}$
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<math.h>
using namespace std;

long long a[1005], ans = 0;
long long pow(int base, int n)
{
    long long sum=1;
    while (n)
    {
        if (n&1) sum = base*sum;
        base *= base;
        n >>= 1;
    }
    return sum;
}

int main() {
    long long n, maxc = 0;
    cin >> n;
    for (int i = 1;i <= n;i++) {
        long long num, cnt = 0, nm;
        cin >> num;
        while (num) {//数num的二进制数中有几位是1
            if (num & 1) a[cnt]++;
            //num&1不为0,说明num的二进制数中的最右边那位是1
            //a[cnt]++表示计数第cnt列的1的个数
            cnt++;
            num >>= 1;
            //相当于num/=2
            //num二进制数右移1位
        }
        maxc = max(maxc, cnt);
        //更新最长的二进制数的长度 
    }
    for (int i = 0;i < maxc;i++)
        ans += a[i] * a[i] * pow(2, i);
    cout << ans;
    return 0;
}

【位运算】大吉大利

标签:str   coder   返回值   不能   ref   这一   长度   amp   依次   

原文地址:https://www.cnblogs.com/streamazure/p/12585153.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!