【AtCoder Grand Contest 016 D】XOR Replace

图论,位运算。

题目大意:

给定两个长度为 $n$ 的数列 $a_1,a_2,a_3,\ldots,a_n$ 和 $b_1,b_2,b_3,\ldots,b_n$。

每次可以把 $a$ 中的一个元素变为 $a$ 中所有元素的 xor 和。

问至少要操作多少次,才能使 $a$ 和 $b$ 相等。

无解输出 $-1$。

题解:

令 $a_{n+1}$ 和 $b_{n+1}$ 分别表示 $a$ 和 $b$ 所有元素的 xor 和。那么一次操作相当于交换 $a_i$ 和 $a_{n+1}$。

如果 $a$ 和 $b$ 排序后不完全相等则无解。

我们令值代表点,对于 $i$,若 $a_i\neq b_i$,则我们令 $a_i$ 向 $b_i$ 连一条有向边。

我们假设 $a_{n+1}$ 在一个连通块里面,那么经过边数次移动可以使这个连通块对应好。从一个连通块到另一个连通块需要额外花费 $1$ 次,所以还需要连通块个数 $-1$ 次。

若 $a_{n+1}=b_{n+1}$,我们要单独给它计算成一个连通块。若其已经在连通块中则可以不用管。

Code:

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<vector>
#include<map>
using namespace std;
const int N=1e5+5;
int a[N],b[N],n,X,Y,cnt,tot,head[N],ans;
bool vis[N];
map<int,int>id;
vector<int>va,vb;
struct edge{
    int to,nxt;
}e[N];
inline int get(int x){return id.count(x)?id[x]:id[x]=++tot;}
void dfs(int u){
    vis[u]=1;for(int i=head[u];i;i=e[i].nxt)if(!vis[e[i].to])dfs(e[i].to);
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=0;i<n;++i)cin>>a[i],X^=a[i],va.push_back(a[i]);
    for(int i=0;i<n;++i)cin>>b[i],Y^=b[i],vb.push_back(b[i]);
    va.push_back(X),vb.push_back(Y);
    sort(va.begin(),va.end()),sort(vb.begin(),vb.end());
    if(va!=vb){cout<<"-1\n";return 0;}
    for(int i=0;i<n;++i)
    if(a[i]!=b[i]){
        int A=get(a[i]),B=get(b[i]);
        e[++cnt]=(edge){B,head[A]},head[A]=cnt;
        ++ans;
    }
    if(!id.count(X))++ans;else
    if(X!=Y){
        int A=get(X),B=get(Y);
        e[++cnt]=(edge){B,head[A]},head[A]=cnt;
    }
    for(int i=1;i<=tot;++i)if(!vis[i])++ans,dfs(i);
    cout<<ans-1<<'\n';
    return 0;
}