[AGC016D] XOR Replace 题解

题意:

一个序列 \(a\),一次操作可以将某个位置变成整个序列的异或和。 求最少几步到达目标序列 \(b\)

\(n \le 10^5\)

思路:

见到这种题,第一步要去尝试把操作转化。

稍微推一下可以发现,如果 \(\oplus_{i=1}^n a_i = s\),则相当于一个 \(n + 1\) 长序列,\(a_{n+1}=s\),每次可以交换 \(a_i\)\(a_{n+1}\),求最少几次交换使得 \(a_1 \sim a_n\) 等于 \(b_1 \sim b_n\)

无解说明又不包含的元素,这个可以直接判断。

考虑最终的答案,肯定是从 \(b_i \to x_1 \to x_2 \to \dots x_k \to a_i\) 的一条路径,所以我们不妨尝试将 \(b_i \to a_i\) 视作一条边。

所以我们要找的便是这张图中的一条从 \(s\) 出发的一条最短欧拉路径,如果这张图联通,因为我们现在是已经有解了,所以最短长度等于边数。

如果图不连通,则说明我们需要额外新增一下边使得图联通且存在欧拉路径,假设有 \(c\) 个联通分支,我们先弄 \(s\) 在的那个,然后是第二个,第三个以此类推,每次只需要多增加一条边即可。

假设我们连了 \(m\) 条边,则答案就是 \(m + e - 1\)

为何是最小?首先,对于连通块内部的路径已经是最短的了,其次,将 \(c\) 个连通块变成一个也至少要 \(c - 1\) 条边,所以这个就是最优解。

点击查看代码
#include <iostream>
#include <vector>
#include <map> 
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;

int n, a[N] = {0}, b[N] = {0};

map<int, int> mp;

vector<int> e[N];

bool vis[N] = {false};
void dfs(int x) {
	vis[x] = true;
	for (auto i: e[x])
		if (!vis[i])
			dfs(i);
}

int main() {
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i], mp[a[i]]++, a[0] ^= a[i];
	for (int j = 1; j <= n; j++)
		cin >> b[j], mp[b[j]]--;
	mp[a[0]]++;
	for (auto i: mp)
		if (i.second < 0) {
			cout << -1;
			return 0;
		}
	int cur = 0;
	for (auto &i: mp)
		i.second = ++cur;
	a[0] = mp[a[0]];
	int ed = 0;
	for (int i = 1; i <= n; i++) {
		a[i] = mp[a[i]];
		b[i] = mp[b[i]];
		if (a[i] != b[i]) {
			e[b[i]].push_back(a[i]);
			e[a[i]].push_back(b[i]);
			ed++;
		}
	}
	
	int cnt = 0;
	for (int i = 1; i <= cur; i++)
		if (!vis[i] && (int)e[i].size() > 0)
			dfs(i), ++cnt;
	if ((int)e[a[0]].size() > 0)
		cnt--;
	cout << ed + cnt << endl;
	return 0;
} 
posted @ 2024-03-05 22:04  rlc202204  阅读(16)  评论(0)    收藏  举报