agc016D - XOR Replace(图论 智商)
程序员文章站
2022-03-25 21:46:03
题意 "题目链接" 给出两个长度为$n$的数组$a, b$ 每次可以将$a$中的某个数替换为所有数$xor$之和。 若$a$数组可以转换为$b$数组,输出最少操作次数 否则输出$ 1$ Sol 一般那看到这种$N \leqslant 10^5$而且不可做的题肯定是先找结论啦 不难看出,我们把所有数$ ......
题意
给出两个长度为$n$的数组$a, b$
每次可以将$a$中的某个数替换为所有数$xor$之和。
若$a$数组可以转换为$b$数组,输出最少操作次数
否则输出$-1$
sol
一般那看到这种$n \leqslant 10^5$而且不可做的题肯定是先找结论啦
不难看出,我们把所有数$xor$起来的数替换掉之后再次$xor$,得到的一定是被替换掉的数。
实际上,我们可以把xor出来的数放到一个新的位置$n+1$,这样每次操作就变成了交换第$n+1$个位置的数和任意一个位置$x$的数
总的问题就变成了
给出两个长度为$n+1$的数组$a, b$,每次可以在$a$中交换$\forall i \in [1, n]$位置和$n+1$位置的数,问最少交换几次变为$b$数组
首先把$-1$的情况判掉,很显然,把两个数组排序后,若存在一个位置不相同,则一定无解
否则一定有解。
到这里我就不会了。。。。
官方题解非常神仙。
对于$i$位置,若$a_i \not = b_i$,则向$a_i$到$b_i$连一条边
最终答案 = 总边数 + 联通块数 - 1
想一想为什么,对于联通块内的点,假设其大小为$x$,我们一定可以通过$x-1$次操作把他们对应的$a$和$b$变的相同
对于不同联通块之间,我们还需要一步操作使得第$n+1$个位置的数在两个联通块之间转化(第一个除外)
对于第$n+1$个位置需要单独考虑:如果它已经在联通块里则不需要考虑,否则把它看做单独联通块
否则
2 1 3 3 1
可以用并查集维护联通块个数
#include<bits/stdc++.h> const int maxn = 4e5 + 10; using namespace std; inline int read() { char c = getchar(); int x = 0, f = 1; while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } int n; int a[maxn], b[maxn], ta[maxn], tb[maxn], sa, sb, tot = 0, date[maxn], fa[maxn]; map<int, bool> ti; int find(int x) { return fa[x] == x ? fa[x] : fa[x] = find(fa[x]); } int unionn(int x, int y) { fa[x] = y; } int main() { n = read(); for(int i = 1; i <= n; i++) a[i] = read(), sa ^= a[i]; a[n + 1] = sa; for(int i = 1; i <= n; i++) b[i] = read(), sb ^= b[i]; b[n + 1] = sb; n++; memcpy(ta, a, sizeof(a)); memcpy(tb, b, sizeof(b)); sort(ta + 1, ta + n + 1); sort(tb + 1, tb + n + 1); for(int i = 1; i <= n - 1; i++) if(ta[i] != tb[i]) return puts("-1"), 0; int ans = 0, num = 0; for(int i = 1; i <= n; i++) if(a[i] != b[i] || (i == n)) { date[++num] = a[i]; date[++num] = b[i]; if(i < n)ans++;//最后一块单独考虑 } if(ans == 0) return puts("0"), 0; sort(date + 1, date + num + 1); num = unique(date + 1, date + num + 1) - date - 1; for(int i = 1; i <= num; i++) fa[i] = i; for(int i = 1; i <= n; i++) if(a[i] != b[i]) { a[i] = lower_bound(date + 1, date + num + 1, a[i]) - date, b[i] = lower_bound(date + 1, date + num + 1, b[i]) - date; if(!ti[a[i]]) ti[a[i]] = 1; if(!ti[b[i]]) ti[b[i]] = 1; unionn(find(a[i]), find(b[i])); } for(int i = 1; i <= num; i++) if(fa[i] == i) ans++; printf("%d", ans - 1); return 0; }