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;
}

转载于:https://www.cnblogs.com/mangoyang/p/9813220.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值