Description
给出一个长度为\(n\)的非负整数数组\(a\),令\(b\)数组为\(a\)数组的前缀异或和,即\(b_i=a_1 \oplus a_2 \oplus \cdots \oplus a_i\),这样做\(m\)次,输出最终的\(b\)数组。
Input
第一行给出用例组数\(T\),对于每组用例,第一行给出\(n\)和\(m\),第二行给出\(n\)个非负整数表示\(a\)数组。\(1 \leqslant n \leqslant 2 \times 10^5\),\(1 \leqslant m \leqslant 10^9\)。
Output
对于每组用例,除数\(n\)个整数,表示求\(m\)遍异或和之后的\(b\)数组。
Sample Input
2
1 1
1
3 3
1 2 3
Sample Output
1
1 3 1
Solution
观察求每一遍异或前缀和后一个\(a_i\)对\(b_j\)的贡献,发现每一个\(a_i\)贡献的数量构成杨辉三角,进一步总结后得出,求第\(m\)次异或前缀和后,\(a_i\)对\(b_j\)贡献的数量为\(C(m+j-i-1,j-i)\),\(j \leqslant i\),即关于\(j-i\)的一个二项式系数,因为我们只关注二项式系数的奇偶性,有一个结论,\(C(a,b)\)是奇数当且仅当\(b\)的二进制表示中左右\(1\)的位置是\(a\)的二进制表示中所有\(1\)的位置的子集,即\(a\ and\ b = b\)。于是我们枚举\(j-i\)的值,记为\(k\),从\(0\)到\(n-1\),仅当\(C(m+k-1,k)\)为奇数时更新\(b\)数组,为奇数的二项式系数并不多,可以在时间限制内完成。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 10;
int a[N], b[N];
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", a + i);
memset(b, 0, sizeof(b));
for (int k = 0; k < n; k++)
if ((k & m + k - 1) == k)
for (int i = 1; i + k <= n; i++)
b[i + k] ^= a[i];
for (int i = 1; i < n; i++) printf("%d ", b[i]);
printf("%d\n", b[n]);
}
return 0;
}