我参加了一个算法竞赛。我陷入了一个问题,我在这里问同样的问题。
问题陈述
XOR-sum数组是对该子数组的所有数字进行异或。 给你一个数组,你必须添加所有可能的这样的XOR子数组。
为了更好地理解,问题陈述也是here。
示例
输入
数组: - 1 2
输出: - 6
解释
F(1, 1) = A[1] = 1
,
F(2, 2) = A[2] = 2
和
F(1, 2) = A[1] XOR A[2] = 1 XOR 2 = 3.
因此答案是1 + 2 + 3 = 6.
我的代码
时间复杂度: - O(N ^ 2),(效率低,未在竞赛中获得)
#include<iostream>
using namespace std;
long long int input[100001];
main() {
int T;
int N;
long long int val;
long long int temp = 0;
long long int answer = 0;
cin >> T;
while(T--) {
cin >> N;
for(int i = 0; i < N; i++) {
cin >> val;
temp = temp^val;
answer += temp;
input[i] = temp;
}
for( int i = 0; i < N; i++ ) {
for( int j = i+1; j < N; j++ ) {
answer += input[i]^input[j];
}
}
cout << answer << endl;
answer = 0;
temp = 0;
}
return 0;
}
问题: -
上看到了此问题的最佳解决方案但是在这段代码中,我不明白下面的模块,请帮我理解。
for (int i=0, p=1; i<30; i++, p<<=1) {
int c=0;
for (int j=0; j<=N; j++) {
if (A[j]&p) c++;
}
ret+=(long long)c*(N-c+1)*p;
}
提前致谢。寻找你的回复。
答案 0 :(得分:3)
我认为你遗漏了一些非常重要的细节:
int A[MAXN];// the arrays contain int values
//xor all the elements of the array as you read them
for (int i=1; i<=N; i++) {
scanf("%d", &A[i]);
A[i]^=A[i-1];
}
阅读输入后,您最终会得到:
A[0] = A[0]
A[1] = A[1]^A[0]
...
A[N] = A[N]^A[N-1]^...^A[0]
这是O(N)你得到它是免费的,因为你还是要阅读输入。 这需要注意或XOR部分。现在你只剩下问题的SUM部分了。 你有N个数字(32位),这里是你展示的部分来自:
for (int i=0, p=1; i<30; i++, p<<=1) {
int c=0;
for (int j=0; j<=N; j++) {
if (A[j]&p) c++;
}
ret+=(long long)c*(N-c+1)*p;
}
对于每一位,您将遍历数组并计算数字1并将它们添加到最终结果中。
这部分是O(30 * N),它仍然是线性时间,所以O(N)。这比O(N ^ 2)好。
希望这能为这件事提供一些启示。
答案 1 :(得分:2)
考虑排列在Nx32矩阵中的数字,其中每一行代表数组中的数字,每列代表所有数字的第i位。现在,XOR操作的效果被限制在一列中。因此,我们可以将每列分开,计算该列的XOR和,并将其添加到结果中。
我已将一个专栏分开。如何计算此列中的XOR-sum?
为此,我们计算此列中的1的数量。设c
表示列中的数字1。然后,0的数字将是N - c
。要在列结果中产生1(0对最终结果没有影响),对于c
中的每1个,我们可以从N - c
取0,或者根本不取0。因此,在XOR运算之后,每个1有N - c + 1
种方式产生1。由于有c
1,因此XOR操作后的总数为c * (N - c + 1)
。
每个列对位置i
的最终结果有不同的贡献。因此,将列结果与2^i
(1 << i
)相乘,并将其添加到最终结果中。
for (int i=0, p=1; i<30; i++, p<<=1)
p = 1 << i
。if (A[j]&p) c++;
ret+=(long long)c*(N-c+1)*p;
p = 1 << i
(= 2 ^ i)。CONFESSION:我只解释了代码中的操作。我没有证据证明这将涵盖所有子阵列。
答案 2 :(得分:1)
据我所知,您提供的链接中的代码不是最佳解决方案,甚至也不是可行的解决方案。您从该链接复制的代码似乎有意义,但在复制的代码之前,当值被读入A
时,它们会被在它们之前读入的值进行异或运行:
for (int i = 1; i <= N; i++)
{
scanf("%d", &A[i]);
A[i] ^= A[i - 1];
}
...意思是以下输入:
1
4
1 2 3 4
...会像A
一样存储到A[0]: 00000000000000000000000000000000 = 0
A[1]: 00000000000000000000000000000001 = 1
A[2]: 00000000000000000000000000000011 = 3
A[3]: 00000000000000000000000000000000 = 0
A[4]: 00000000000000000000000000000100 = 4
中:
F(1, 1) + F(1, 2) + F(2, 2) + F(1, 3) + F(2, 3) + F(3, 3) + F(1, 4) + F(2, 4) + F(3, 4) + F(4, 4)
= 1 + 3 + 2 + 2 + 1 + 3 + 5 + 6 + 7 + 4
= 34
对于上一个输入,正确的答案应该是:
c
...但是这是我们使用您发布的“最佳”算法得到的结果(N
*(c
- i
+ 1)的总和* 2 ^ { {1}}从i
= 0到i
= 29)
2 * (4 - 2 + 1) * 1 + 1 * (4 - 1 + 1) * 2 + 1 * (4 - 1 + 1) * 4
= 6 + 8 + 16
= 30
所以它已经过了4个,因此不是解决问题的有效方法,更不用说最好的解决方案了。
请注意,如果读取的值 >> 未被异或,则A
中的内容为
A[0]: 00000000000000000000000000000000 = 0
A[1]: 00000000000000000000000000000001 = 1
A[2]: 00000000000000000000000000000010 = 2
A[3]: 00000000000000000000000000000011 = 3
A[4]: 00000000000000000000000000000100 = 4
那么从c
= 0到{{1}的N
*(c
- i
+ 1)* 2 ^ i
的公式总和}} = 29会给:
i
...根据您链接的网站上的问题陈述,这是正确的答案。我认为这就是为什么到目前为止我们已经看到了与你发布的代码一致的答案 - 你发布的代码是有意义的,即使前面的代码(在你链接的页面上找到)也会导致整个程序出错。
答案 3 :(得分:1)
This solution 你提到的很棒!
让我向您解释这个解决方案。
假设数组 a 从索引 1 开始。 让 s[i] = a[1]^a[2]^.....^a[i]。 (让 s[0] = 0)。
任何子数组 [L, R] 的异或将是 s[R]^s[L-1]。
现在要找到所有子数组的异或之和,我们只需要找到数组中所有元素对的异或之和 s[0], s[1], s[2], .... s[n].
对此的解决方案已经得到解答here。 您也可以查找gfg's solution for latter problem。