Jzoj P5910 DuLiu___思维+并查集

题目大意:

给出一个 n n n 个数的序列 a a a,每次操作可以将一个数变成整个序列的值的异或
求最少需要多少次才能将 a a a 变成目标序列 b b b
无法完成输出 − 1 -1 1

1 &lt; = N &lt; = 100000 1&lt;=N&lt;=100000 1<=N<=100000
数 的 大 小 &lt; 2 30 数的大小&lt;2^{30} <230

分析:

考虑操作的本质
只要按位稍微分析一下,就可以发现,题目相当于一开始手里抓着整个序列 a 的异或,
一次操作可以将手上的数与序列中的某个数换过来
如果能够完成,方便起见将 a 的异或和 b 的异或分别加到序列末,排序后两个序列显然完全相同,
不相同就无解,
然后我么发现只有 a[i]!=b[i]的位置我们是需要调整的
那么将离散数值,并将a[i]与 b[i]连边
考虑统计答案
将一个联通块内换完所需要的次数明显就是联通块大小。
而联通块之间跳需要 1 的代价
此时由于我们一开始手上抓着的是 a 的异或和
如果它刚好在某一个联通块里面就不用考虑了,否则就必须将它自己看做一个大小为 0 的联通块
联通块可以用并查集维护

代码:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue> 
#include <map>
#define N 100005

using namespace std;

int a[N], b[N], c[N], d[N], fa[N*2];
int n, m, num, ans;
map <int, int> rp;

int Find(int x)
{
    if (fa[x] == x) return x;
    return (fa[x] = Find(fa[x]));
}
int main()
{
	freopen("duliu.in", "r", stdin);
    freopen("duliu.out", "w", stdout);
	scanf("%d", &n); int tot1 = 0, tot2 = 0;
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]), tot1 ^= a[i]; a[n + 1] = tot1;
	for (int i = 1; i <= n; i++) scanf("%d", &b[i]), tot2 ^= b[i]; b[++n] = tot2;  
    for (int i = 1; i <= n; i++) c[i] = a[i], d[i] = b[i];
    
    sort(c + 1, c + n + 1); sort(d + 1, d + n + 1); 
	bool flag = 1; for (int i = 1; i <= n; i++) if (c[i] != d[i]) flag = 0;
    if (!flag) { printf("-1\n"); return 0; }
    
	for (int i = 1; i < n; i++) if (a[i] != b[i]) ++ans;
    if (!ans) { printf("0\n"); return 0; }
        
	for (int i = 1; i <= n; i++)
		 if (a[i] != b[i] || i == n)
            {
			    if (!rp[a[i]]) rp[a[i]] = ++num;
                if (!rp[b[i]]) rp[b[i]] = ++num;
            }
		
    for (int i = 1; i <= num; i++) fa[i] = i;
    for (int i = 1; i <= n; i++)
        if (a[i] != b[i]) fa[Find(rp[a[i]])] = Find(rp[b[i]]);
    for (int i = 1; i <= num; i++) if (fa[i] == i) ++ans;
    printf("%d\n", ans - 1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值