https://ac.nowcoder.com/acm/contest/120563/I
正好复习一下线性基。
给定一个数组,长度为,设数组中所有数的二进制最高位为
,那么该数组中的数互相异或的结果最多只有
种,显然大多数情况下
。这给我们启发:其实我们可以在
上挑
个数,使原数组所有可能的异或结果都能通过这
个数互相异或得到,且不能得到的结果这
个数也不能通过异或得到(即两个集合完全等价)。
那么考虑如何构造这个数。异或线性基有以下性质:
等价性:在原数组和在线性基上进行异或计算的结果完全相同。
最小性:是满足等价性的所有集合中元素个数最少的。这要求线性基中不存在异或和为的子集。
最重要的:线性基中每个数的二进制最高位不同。
那么就很好构造了。先给代码:
void gz(int x){
for(int i=30;i>=0;i--){
if((x>>i)&1){
if(!p[i]){
p[i]=x;
return;
}
x^=p[i];
}
}
}
void work(){
for(int i=1;i<=n;i++){
gz(b[i]);
}
}
因为最高位各不相同,所以我们从大到小检测最高位,如果当前线性基没有最高位为当前数的,就把它放进线性基;否则当前数就异或线性基中最高位和它相同的数。正确性显然可以使用归纳法证明。简单理解就是:线性基中的所有数要么直接来源于原数组,要么通过原数组中的数之间异或得到。
那么可以做这题了:先得到的异或和
,如果
换
,那么变化量显然是:
^
。那么建立
^
,即求
(这里的
表示异或和)。
那么就可以对建线性基了。因为线性基的计算和
的计算等价,那么就可以用线性基从高到低尝试消去
的每一个二进制位即可,时间复杂度
。
至于答案的维护:记录得到线性基的每一个数用到了原数组的哪些数即可,可以用bitset快速计算。
代码:
#include<bits/stdc++.h>
using namespace std;
int a[1000100],b[1000100],p[100],ue[100],vis[1000100];
bitset<35>bt[35];
int read(){
char ch=getchar();int x=0,f=1;
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
void gz(int x,int pos){
bitset<35>b,c;
for(int i=30;i>=0;i--){
if((x>>i)&1){
if(!p[i]){
p[i]=x;
ue[i]=pos;
c[i]=true;
bt[i]=b^c;
return;
}
x^=p[i];
b^=bt[i];
}
}
}
void work(){
for(int i=0;i<=30;i++){
p[i]=0;ue[i]=0;
bt[i].reset();
}
int n=read();
int res=0;
for(int i=1;i<=n;i++) a[i]=read(),res^=a[i];
for(int i=1;i<=n;i++) b[i]=read()^a[i];
if(!res){
for(int i=1;i<=n;i++){
printf("%d ",a[i]);
}
return;
}
for(int i=1;i<=n;i++){
gz(b[i],i);
}
bitset<35>ans;
for(int i=30;i>=0;i--){
if((res>>i)&1){
if(!p[i]){
puts("-1");
return;
}
res^=p[i];
ans^=bt[i];
}
}
for(int i=0;i<=30;i++){
if(ans[i]){
vis[ue[i]]=1;
}
}
for(int i=1;i<=n;i++){
if(vis[i]){
printf("%d ",b[i]^a[i]);
}else{
printf("%d ",a[i]);
}
vis[i]=0;
}
puts("");
}
int main(){
int t=read();
while(t--){
work();
}
return 0;
}

京公网安备 11010502036488号