题目
链接:https://www.luogu.com.cn/problem/P6102
思路分析
这道题。。。写了好久,看了大佬博客写出来的(太菜了。。。)。
大佬题解:https://www.luogu.com.cn/blog/Feliks-YB/post-ti-xie-p6102-post
这道题是这样做的:每读入一个数,就把这个数转化为32位的二进制数。需要明白一点:or、ans、xor这些运算都只和一个二进制位上的有关系。然后统计下这32位上每一位出现1的次数和出现0的次数(下面需要用到类似概率统计的方法,所以这里统计频数)。根据统计:对于1、0这两个数分为(ai,aj,ak,al)的排列中:
(1,1,0,0),(1,0,1,0),(1,0,0,1),(0,1,0,1)(0,0,1,1)(1,1,1,0)(1,1,0,1)(1,0,0,0)(0,1,0,0)(0,1,1,0)
这10种排列计算的结果为1。那么,对于输入的这n个数,我们去它最右边的二进制位的统计结果,记1的频数为p1,0的频数为p0,则:这一位上得到1的频数为p1p1p0p06(6为在上面包含2个1和2个0的组合的个数为6)+p1p0p0p02(2和上面的6是一个道理)+p0*p1p1p12(同理),这是一位上1的频数。
我们再来看看相加的时候:按照二进制加法,相加的时候其实就是二进制上对应位置上的数相加,满1就向前进一位,那么,我们相加的时候也可以用相同的手法:我们统计出了每一位上1的频数,那么我们每一位分别处理:
假设第一位上1的频数为a1,下面一次类推,则:第一位上结果为a1*1,第二位上是a21……那么,我们组成的这个数就是:
a32a31……a2a1(但是每一位上还需要进位)
例如a1=5,a2=3,其他都是0,那么,这个数就是
000……32
然后,再满2进1一下,这个是就是00000……1000,转为10进制就是2^3=8,这个就是我们的答案。那么,这个相加的过程,我们就可以用位运算符<<帮我们实现。
完整代码
#include <iostream>
using namespace std;
const long long mod = 4294967296;
typedef long long ll;
unsigned int a[500005] = { 0 };
int cnt[32][2]={0};
int main()
{
ios::sync_with_stdio(false);
ll n;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
for(int j=0;j<32;j++)
{
if(a[i]&(1<<j)) cnt[j][1]++;
else cnt[j][0]++;
}
}
unsigned ans=0;
for(int i=0;i<32;i++)
{
unsigned int res=(cnt[i][1]*cnt[i][1]*cnt[i][0]*cnt[i][0])*6;
res+=(cnt[i][1]*cnt[i][0]*cnt[i][0]*cnt[i][0])<<1;
res+=(cnt[i][0]*cnt[i][1]*cnt[i][1]*cnt[i][1])<<1;
ans+=res<<i;
}
cout << ans << endl;
return 0;
}