专栏文章
题解:AT_agc016_d [AGC016D] XOR Replace
AT_agc016_d题解参与者 4已保存评论 4
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 4 条
- 当前快照
- 1 份
- 快照标识符
- @miogpjno
- 此快照首次捕获于
- 2025/12/02 18:55 3 个月前
- 此快照最后确认于
- 2025/12/02 18:55 3 个月前
本文作者 @tanglb,是校内模拟赛 T2 题解,经过作者同意后代为交到原题 AT_agc016_d 题解区。有修改。
先模拟一下这个操作:
设 ,当对 操作时,。
由此发现,每次对 进行操作,等价于交换 和 。而交换不会有新的数产生,操作后 中的数只能是 和 这 个数中的 个,且每种排列顺序都能取到。那么无解的情况就很好分辨了,直接判断 数组是否包含于这 个数组成的可重集中即可。
现在我们保证有解,如何确定最小步数?
贪心的想,每一次交换都应尽量匹配一组 。当 时交换 和 , 时再交换 和 ,如此往复,我们就能匹配若干对 并且每一步都不浪费。
上面不断交换的过程中, 决定了下一次交换的位置,不妨让每个 向 建边,将交换过程转化为图上的 dfs,交换次数就为走过的边数,所有 仅当图上每一条边都走过。
建出的图可能不连通,当我们要从一个连通块跳到另一个连通块时,可以通过加一条边来实现。例如从 到 没有边,交换 与 等价于建 到 的边,并跳到 的位置。每跳一次边数就加一。
根据上文结论,最小操作次数等价于边数,设连通块数为 ,则答案为 。维护连通块可以建边后 dfs 或者使用并查集,注意特判图上的自环,这些自环不用走,不需要算入操作次数。
这里放一个并查集做法的代码。
CPP#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[200005];
int b[200005];
int ans;
map<int,int>mp;
map<int,int>fa;
int find(int x){
if(fa[x]==0)return fa[x]=x;
return(x==fa[x]?x:fa[x]=find(fa[x]));
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(),cout.tie();
int n;
cin>>n;
int flc=0;
for(int i=1;i<=n;i++)
cin>>a[i],mp[a[i]]++,a[n+1]^=a[i];
for(int i=1;i<=n;i++)
cin>>b[i],mp[b[i]]--;
mp[a[n+1]]++;
int sum=0;
for(auto i:mp)
if(i.second)sum++;
if(sum!=1){
puts("-1");
return 0;
}
for(int i=1;i<=n;i++)
if(a[i]==b[i])flc--;
for(int i=1;i<=n+1;i++)a[i]++;
for(int i=1;i<=n;i++)b[i]++;
for(int i=1;i<=n;i++)
fa[find(a[i])]=find(b[i]);
sum=0;
mp.clear();
for(int i=1;i<=n;i++){
if(a[i]==b[i])continue;
if(find(b[i])==b[i]&&!mp[b[i]])sum++,mp[b[i]]=1;
}
for(int i=1;i<=n;i++)
if(b[i]==a[n+1]&&a[i]!=b[i]){
sum--;
break;
}
cout<<n+sum+flc;
return 0;
}
// 关注 fish_love_cat (uid=754021) 谢谢喵
相关推荐
评论
共 4 条评论,欢迎与作者交流。
正在加载评论...