AtcoderGrandContest 016 D.XOR Replace

$ >AtcoderGrandContest \space\ 016\ D.XOR\space Replace<$

题目大意 :
有两个长度为 \(n\) 的数组 \(A, B\) ,每次操作将 \(A\) 中的一个元素替换为 \(A\) 中所有元素的异或和,求最少几次操作可以从 \(A\) 变换到 \(B\)

\(1 \leq n \leq 10^5\)

解题思路 :

设初始状态的异或和为 \(S\) ,第一次替换掉的数为 \(x\) ,容易发现新的数列的异或和为 \(x\) ,可以简单证明每一次的异或和等于上一次被替换的数。

那么问题就转化为对于一个序列 \(A\)\(A_{n+1} = S\) ,第一次取出 \(S\) ,之后每一次替换掉某个数并取出它来替换别人,事实上每一个取出的数都会被放到其在 \(B\) 中的位置上。

此时 \(A\) 数组就构成了若干个置换,最小花费就是置换的数量加上环上的点数 \(-1\) ,但是一个数可能会有多个合法的位置可以选,直接构造的话置换的数量可能不会优。

但是观察发现如果两个置换存在数值相等的位置,那么可以通过交换 \(next\) 指针变成一个置换,所以只需要按照数值来合并联通块就能求出置换的最小数量,可以用并查集来维护。


/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
	int f = 0, ch = 0; x = 0;
	for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
	for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
	if(f) x = -x;
}
const int N = 1000005;
map<int, int> id;
int fa[N], s1[N], s2[N], a[N], b[N], n, col, ans;
inline int ask(int x){ return x == fa[x] ? x : fa[x] = ask(fa[x]); }
inline void merge(int x, int y){
	int p = ask(x), q = ask(y); if(p != q) fa[p] = q;
}
signed main(){
	read(n);
	for(int i = 1; i <= n; i++) read(a[i]), a[n+1] ^= a[i];
	for(int i = 1; i <= n; i++) read(b[i]), b[n+1] ^= b[i];
	for(int i = 1; i <= n + 1; i++) s1[i] = a[i], s2[i] = b[i];
	sort(s1 + 1, s1 + n + 2), sort(s2 + 1, s2 + n + 2);
	for(int i = 1; i <= n + 1; i++) if(s1[i] != s2[i]) return puts("-1"), 0;
	for(int i = 1; i <= n; i++) if(a[i] != b[i]) ans++;
	for(int i = 1; i <= n + 1; i++) fa[i] = i;
	for(int i = 1; i <= n + 1; i++) if(a[i] != b[i]){
		if(!id[a[i]]) id[a[i]] = ++col;
		if(!id[b[i]]) id[b[i]] = ++col;
		merge(id[a[i]], id[b[i]]);
	}
	if(!id[a[n+1]]) id[a[n+1]] = ++col;
	if(!id[b[n+1]]) id[b[n+1]] = ++col;
	for(int i = 1; i <= col; i++) if(fa[i] == i) ans++;
	cout << ans - 1;
	return 0;
}
posted @ 2018-10-18 21:15  Joyemang33  阅读(180)  评论(0编辑  收藏  举报