文章目录
异或积
序言
一个思考题,很水,但比赛时没过。传送门
题目背景
id: 4d7e \texttt{id: 4d7e} id: 4d7e
小 H 在课堂上学习了异或运算。
对于两个非负整数 x , y x,y x,y,它们的异或是指,将它们作为二进制数,对二进制表示中的每一位进行如下运算得到的结果:
- x x x 和 y y y 的这一位上不同时,结果的这一位为 1 1 1;
- x x x 和 y y y 的这一位上相同时,结果的这一位为 0 0 0。
x x x 和 y y y 的异或被记为 x xor y x \operatorname{xor} y xxory 或 x ⊕ y x \oplus y x⊕y。
在 C++ 中,你可以用 x ^ y
得到
x
x
x 与
y
y
y 的异或值。
另外,若干个数的异或称之为异或和。
题目描述
小 H 还了解到,一个长度为 n n n 的数列 a a a 的异或积是一个等长的数列 b b b,其中 b i b_i bi 等于数列 a a a 中除了 a i a_i ai 以外其他元素的异或和,即
b i = ⨁ j = 1 n [ j ≠ i ] a j b_i = \bigoplus \limits_{j = 1}^{n} [j\ne i] a_j bi=j=1⨁n[j=i]aj
例如,数列 { 1 , 2 , 3 , 4 } \{1, 2, 3, 4\} {1,2,3,4} 的异或积为 { 5 , 6 , 7 , 0 } \{5, 6, 7, 0\} {5,6,7,0}。
异或积变换是指将一个数列用它的异或积替换的过程,由于异或积变换之后数列长度不变,所以异或积变换可以连续进行多次。
现在,小 H 有一个长度为 n n n 的数列 a a a,他想请你帮他计算出 a a a 经过 k k k 次异或积变换之后得到的序列。
输入格式
本题单个测试点内有多组测试数据。
第一行一个整数 T T T,表示测试数据组数。
对于每一组测试数据:
第一行两个整数 n , k n,k n,k。
第二行 n n n 个整数 a 1 , a 2 , ⋯ , a n a_1,a_2,\cdots,a_n a1,a2,⋯,an。
输出格式
对于每一组测试数据:
一行 n n n 个整数,表示数列 a a a 经过 k k k 次异或积变换之后得到的数列。
样例 1
样例输入 1
1
4 1
1 2 3 4
样例输出 1
5 6 7 0
样例 2
样例输入 2
1
4 2
0 0 0 1
样例输出 2
0 0 0 1
提示
样例 1 解释
此样例即为题目描述中的例子。
样例 2 解释
第 1 1 1 次异或积变换: { 0 , 0 , 0 , 1 } → { 1 , 1 , 1 , 0 } \{0,0,0,1\}\to\{1,1,1,0\} {0,0,0,1}→{1,1,1,0};
第 2 2 2 次异或积变换: { 1 , 1 , 1 , 0 } → { 0 , 0 , 0 , 1 } \{1,1,1,0\}\to\{0,0,0,1\} {1,1,1,0}→{0,0,0,1}。
数据规模与约定
对于 100 % 100\% 100% 的测试数据, 1 ≤ T ≤ 10 1 \le T \le 10 1≤T≤10, 2 ≤ n ≤ 1 0 5 2 \le n \le 10^5 2≤n≤105, 1 ≤ k ≤ 1 0 18 1 \le k \le 10^{18} 1≤k≤1018, 0 ≤ a i < 2 32 0 \le a_i < 2^{32} 0≤ai<232。
测试点编号 | n ≤ n\leq n≤ | k ≤ k \leq k≤ | 特殊性质 |
---|---|---|---|
1 ∼ 3 1 \sim 3 1∼3 | 100 100 100 | 100 100 100 | |
4 ∼ 5 4 \sim 5 4∼5 | 1000 1000 1000 | 1000 1000 1000 | |
6 ∼ 7 6 \sim 7 6∼7 | 3 3 3 | 1 0 18 10^{18} 1018 | |
8 ∼ 10 8 \sim 10 8∼10 | 1 0 5 10^5 105 | 3 3 3 | |
11 ∼ 13 11 \sim 13 11∼13 | 1 0 5 10^5 105 | 1 0 18 10^{18} 1018 | a a a 中所有数的异或和为 0 0 0 |
14 ∼ 15 14 \sim 15 14∼15 | 1 0 5 10^5 105 | 1 0 18 10^{18} 1018 | n n n 为奇数 |
16 ∼ 17 16 \sim 17 16∼17 | 1 0 5 10^5 105 | 1 0 18 10^{18} 1018 | n n n 为偶数 |
18 ∼ 20 18 \sim 20 18∼20 | 1 0 5 10^5 105 | 1 0 18 10^{18} 1018 |
提示
在 C++ 中,对于数据范围 0 ≤ x < 2 32 0\le x<2^{32} 0≤x<232,你可以:
- 使用
unsigned int x
来定义; - 使用
cin >> x
或scanf("%u", &x)
来输入; - 使用
cout << x
或printf("%u", x)
来输出。
思路初探
1.数据范围
首先,数据范围上看 1 ≤ k ≤ 1 0 18 1 \le k \le10^{18} 1≤k≤1018很明显 1 s e c o n d 1 second 1second是跑不完的,于是开始考虑数学方法…
2.数学思考
书接上回,我们考虑数学方法:
先来四个推一下(推 N N N为偶数)
{
a
1
,
a
2
,
a
3
,
a
4
}
\{a_1,a_2,a_3,a_4\}
{a1,a2,a3,a4}
第一次异或后:
{
a
2
⨁
a
3
⨁
a
4
,
a
1
⨁
a
3
⨁
a
4
,
a
1
⨁
a
2
⨁
a
4
,
a
1
⨁
a
2
⨁
a
3
}
\{a_2 \bigoplus a_3 \bigoplus a_4,a_1 \bigoplus a_3 \bigoplus a_4,a_1 \bigoplus a_2 \bigoplus a_4,a_1 \bigoplus a_2 \bigoplus a_3\}
{a2⨁a3⨁a4,a1⨁a3⨁a4,a1⨁a2⨁a4,a1⨁a2⨁a3}
第二次:
{
a
1
⨁
a
3
⨁
a
4
⨁
a
1
⨁
a
2
⨁
a
4
⨁
a
1
⨁
a
2
⨁
a
3
.
.
.
}
\{a_1 \bigoplus a_3 \bigoplus a_4\bigoplus a_1 \bigoplus a_2 \bigoplus a_4\bigoplus a_1 \bigoplus a_2 \bigoplus a_3... \}
{a1⨁a3⨁a4⨁a1⨁a2⨁a4⨁a1⨁a2⨁a3...}自己打,我懒
化简:
{
a
1
,
a
2
,
a
3
,
a
4
}
\{a_1,a_2,a_3,a_4\}
{a1,a2,a3,a4}
所以推出:奇数次变换都相等,偶数次变换也都相等(但这只是
N
N
N为偶数的情况)
继续推(推 N N N为奇数)
{
a
1
,
a
2
,
a
3
}
\{a_1,a_2,a_3\}
{a1,a2,a3}
第一次
{
a
2
⨁
a
3
,
a
1
⨁
a
3
,
a
1
⨁
a
2
}
\{a_2\bigoplus a_3,a_1\bigoplus a_3,a_1\bigoplus a_2\}
{a2⨁a3,a1⨁a3,a1⨁a2}
第二次
{
a
1
⨁
a
3
⨁
a
1
⨁
a
2
,
a
2
⨁
a
3
⨁
a
1
⨁
a
2
,
a
2
⨁
a
3
⨁
a
1
⨁
a
3
}
\{a_1\bigoplus a_3\bigoplus a_1\bigoplus a_2,a_2\bigoplus a_3\bigoplus a_1\bigoplus a_2,a_2\bigoplus a_3\bigoplus a_1\bigoplus a_3\}
{a1⨁a3⨁a1⨁a2,a2⨁a3⨁a1⨁a2,a2⨁a3⨁a1⨁a3}
化简
{
a
2
⨁
a
3
,
a
1
⨁
a
3
,
a
1
⨁
a
2
}
\{a_2\bigoplus a_3,a_1\bigoplus a_3,a_1\bigoplus a_2\}
{a2⨁a3,a1⨁a3,a1⨁a2}
所以推出: 除原本的式子外,其他都一样(奇数情况)
3.思考
因为,奇数次变换都相等,偶数次变换也都相等(
N
N
N为偶数的情况); 除原本的式子外,其他都一样(奇数情况)。
所以,我们是不是可以只算出第一次变换然后判断奇偶数
说做就做…
4.实现(代码)
#include<bits/stdc++.h>
using namespace std;
long long t,n,k;
long long a[1000001];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin>>t;
while(t--) {
cin>>n>>k;
for(int i=1; i<=n; i++) {
cin>>a[i];
}
if((n%2==0 && k%2==1) || (n%2!=0 && k!=0)) {
//判断;如果N为偶数,K为奇数,则输出第一次变换;如果N为奇数则输出第一次变换,个人觉得应该判断K是否为0,K为0,则输出原式,不判断也能过
// cout<<1<<"\n";用来检查不用管
long long s=0;
for(int i=1; i<=n; i++) {
s^=a[i];
}
for(int i=1; i<=n; i++) {
long long h=s^a[i];
cout<<h<<" ";
}
} else {
//其余输出原式
// cout<<2<<"\n";用来检查不用管
for(int i=1; i<=n; i++) {
cout<<a[i]<<" ";
}
}
cout<<"\n";
}
}