CF2192E Swap to Rearrange 题解
先考虑最终每个数组中所有数互不相同。
一个显然的必要条件是所有数都出现了偶数次。考虑某时刻 \(x\) 在某个数组,那会在这个数组里寻找 \(x\),如果找到了就把它换走,并对这个位置在另一个数组中的对应位置的元素做同样的过程。
发现这个过程非常像在图上走路,考虑图论建模。把每一种权值建成一个点,依据上面的过程,我们在 \((a_i,b_i)\) 之间建无向边。
注意到在上面过程中,每走一步 \(x\) 所在的数组是交替变化的,也就是说同一条边两端的元素所处的数组不同。又由于我们需要保证两个数组中同一种数数量相同,因此离开这个点时我们需要产生一个表示这个点在另一个数组的状态。这两条一起提示我们考虑对边定向。
考虑利用边的走过的方向来表示交换情况:对于一条边,入点视为放在 \(a_i\),出点视为放在 \(b_i\)。显然每个对应位置只会被考虑恰好一次,因此我们需要遍历每条边恰好一次,这提示我们欧拉路径。
考虑走路的过程,由于数互不相同,每个点必然是一进一出,那一条路径必然构成一个环。因此,原问题等价于对于 \((a_i,b_i)\) 建边后的无向图存在欧拉回路。构造方案的话,对每条边记录一个本来的方向,把欧拉回路求出来,如果欧拉回路中的边与这条边本来的方向不同就翻转对应的这一位。
不难发现这个做法扩展到最终有相同的数也是对的,等价于一个点多连了一些边,表示这个值的位置在那个数组被考虑多次。
一个有意思的事情是欧拉回路需要判定所有点的度数都是偶数,而你发现每个点的度数正是一开始提到的一个数的出现次数。这进一步说明了这个建模的正确性。
#include <bits/stdc++.h>
using namespace std;
struct edge
{
int v,nxt,p,fl;
}e[3000000];
int t,n,a[2000000],b[2000000],c[2000000],h[2000000],pr[2000000],cnt=1;
bool ou[2000000],vis[3000000];
void add_edge(int u,int v,int p,int fl)
{
e[++cnt].nxt=h[u];
e[cnt].v=v;
e[cnt].p=p,
e[cnt].fl=fl;
h[u]=cnt;
}
void dfs(int x)
{
for(int i=pr[x];i;i=pr[x])
{
pr[x]=e[i].nxt;
if(!vis[i])
{
vis[i]=vis[i^1]=1;
if(e[i].fl==1)ou[e[i].p]=1;
dfs(e[i].v);
}
}
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)c[i]=h[i]=0,ou[i]=0,cnt=1;
for(int i=1;i<=n;i++)scanf("%d",&a[i]),c[a[i]]++;
for(int i=1;i<=n;i++)scanf("%d",&b[i]),c[b[i]]++,add_edge(a[i],b[i],i,0),add_edge(b[i],a[i],i,1);
for(int i=1;i<=n;i++)pr[i]=h[i];
bool flag=0;
for(int i=1;i<=n;i++)
if(c[i]&1)flag=1;
if(flag)printf("-1\n");
else
{
for(int i=1;i<=cnt;i++)vis[i]=0;
for(int i=1;i<=n;i++)dfs(i);
int ans=0;
for(int i=1;i<=n;i++)
if(ou[i])ans++;
printf("%d\n",ans);
for(int i=1;i<=n;i++)
if(ou[i])printf("%d ",i);
printf("\n");
}
}
return 0;
}
感谢这题送我上 Candidate Master。

浙公网安备 33010602011771号