异或和快速求法
题目描述
给定一个数组[a1,a2,a3…an][a_1, a_2, a_3 \dots a_n][a1,a2,a3…an] , 定义f(i,j)f(i,j)f(i,j) 为 aiXORaja_i\quad XOR\quad a_jaiXORaj
求任意 i,ji,ji,j , f(i,j)f(i,j)f(i,j) 的和
比较传统的做法
传统做法就是直接暴力枚举,即双重循环
#include<bits/stdc++.h>
using namespace std;
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int Sum = 0;
for(int temp = 0 ; temp < 10 ; temp++){
for(int temp2 = temp ; temp2 < 10; temp2++){
if(temp == temp2) continue;
Sum += (arr[temp] ^ arr[temp2]);
}
}
cout << Sum;
return 0;
}
// 339
但此时复杂度来到了不能接受的 O(n2)O(n^2)O(n2) , 所以我们有了一个更优的算法
更优的算法
众所周知,异或是一个位运算,所以我们可以将要异或的数展开来看:
拿 [1,2,3,4,5,6,7,8,9,10][1,2,3,4,5,6,7,8,9,10][1,2,3,4,5,6,7,8,9,10] 举例:
111 [[[ 000 000 000 111 ]]] 222 [[[ 000 000 111 000 ]]]
333 [[[ 000 000 111 111 ]]] 444 [[[ 000 111 000 000 ]]]
555 [[[ 000 111 000 111 ]]] 666 [[[ 000 111 111 000 ]]]
777 [[[ 000 111 111 111 ]]] 888 [[[ 111 000 000 000 ]]]
999 [[[ 111 000 000 111 ]]] 101010 [[[ 111 000 111 000 ]]]
之后我们统计每一个位上 111 的个数:
[[[ 333 444 555 555 ]]]
我们可以知道,这些数异或和的值取决于每个位(1,0)(1,0)(1,0)对的个数,所以我们对每个位计算(1,0)(1,0)(1,0)对的个数
根据乘法法则得:
[[[ (3×7)(3\times 7)(3×7) (4×6)(4\times6)(4×6) (5×5)(5\times 5)(5×5) (5×5)(5\times5)(5×5) ]]]
所以他们的异或和为:
21×23+24×22+25×21+25×20=33921\times 2^3 + 24\times 2^2 + 25\times 2^1 + 25\times 2^0 = 33921×23+24×22+25×21+25×20=339
代码
#include<bits/stdc++.h>
using namespace std;
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int Count[10];
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
for(int temp = 0 ; temp < 10 ; temp++){
for(int temp2 = 0 ; temp2 <= 5 ; temp2++){
if((arr[temp] >> temp2) & 1)
Count[temp2] ++;
}
}
int Sum = 0;
for(int temp = 0 ; temp <= 5 ; temp++)
Sum += ((Count[temp] * (10 - Count[temp])) * (1 << temp));
cout << Sum;
return 0;
}
XOR补充知识
x⊕y=z⇒x⊕z=yx\oplus y = z \Rightarrow x \oplus z = yx⊕y=z⇒x⊕z=y
(x⊕y)⊕z=x⊕(y⊕z)(x \oplus y) \oplus z = x \oplus (y \oplus z)(x⊕y)⊕z=x⊕(y⊕z)
x⊕0=xx⊕x=0x \oplus 0 = x \quad\quad x\oplus x = 0x⊕0=xx⊕x=0
x⊕b⊕b=x⊕0=xx \oplus b \oplus b = x \oplus 0 = xx⊕b⊕b=x⊕0=x
完结撒花