CF1983D 题解

· · 题解

前言

大佬们的思路似乎都是基于逆序对考虑的,这里给出另一种思考的角度。

题意简述

给定长度为 n 的两个数组 ab

每次操作,可以选出四个数 l,r,x,y 满足 1 \leq l \leq r \leq n , 1 \leq x \leq y \leq n,r-l=y-x。将 a_l,a_r 交换,b_x,b_y 交换。

问是否能在若干次操作后是的 a,b 两数组相同。

# 思路 刚读到这个题,感觉是一道构造性质的题目,于是进行了一番手玩。 首先如果 **$a,b$ 两数组的元素组成不同**,直接**输出 `NO`** ,故接下来的讨论基于两数组的元素组成相同。 而注意到对于任意 $n$ 的排列 $p$ ,我们总能通过一些 $l=x,r=y$ 的操作使得数组 $a$ 与数组 $b$ 分别变为以 $p_i$ 为下标的排序方式,故接下来我们能够**任意调换任意一对 $a_i,b_i$ 的相对位置**。 接下来我们定义: 若存在一个集合 $\{s_1,s_2,...,s_k\}$,满足集合 $\{a_{s_1},a_{s_2},...,a_{s_k} \}$ 与集合 $\{b_{s_1},b_{s_2},...,b_{s_k} \}$ 相同,且对于任意包含 $s_1$ 的集合,$k$ 为最小值,则称下标 $\{s_1,s_2,...,s_k\}$ **组成一个 $k$ 元环**。 举例来说,对于下面一种情况: ``` 1 2 3 4 5 6 7 8 9 2 3 1 6 4 5 8 7 9 {1,2,3}组成3元环 {4,5,6}组成3元环 {7,8}组成2元环 {9}组成1元环 ``` 这道题的本质实际上就是**判断是否能将两数组匹配为 $n$ 个 $1$ 元环**。 下面我们来手玩一些情况。 首先对于这个: ``` 1 2 2 1 ``` 这是一个 **$2$ 元环**,是在样例中也出现了的**死局**,实际上这也是整道题目当中**唯一可能出现的无解的情况**。 紧接着,我们钦定一种操作方案,我们可以进行 $n-2$ 组操作,第 $i$ 组操作的目标是**将 $b$ 数组中的 $i$ 达到正确的位置**。 他们均可以使用: - 先将 $b$ 数组中的 $i$ 移动到 $i+1$ 的位置 - 再将其移动到 $i$ 位置。 达成目标。 (由于每对数字的相对位置可以任意调换,这里省去了每组操作后对 $a$ 数组重新排序的步骤。) 这个构造方案还是很容易能得到的,不过下面还是给出证明,觉得简单可以直接跳: 对于第一步,由于前 $i-1$ 次操作保证了 $b$ 中 $1$ 到 $i-1$ 均已正确排序,则令 $t_i$ 为当前 $i$ 在数组 $b$ 当中的位置,很显然 $t_i>i$ 。我们可以至少选择 $l=x=i+1,r=y=t_i$进行一次操作,达成目标。 对于第二步,我们只需要选择 $l=n-1,r=n,x=i,y=i+1$ 即可达成目标。 故我们只需要判断最终操作后的末两位是否匹配即可,这是容易观察得到的。所以我们的构造方案出现的 $k$ 元环当中保证了 $k \leq 2$ 且 $2$ 元环最多只有一个。 接下来是这个 $3$ 元环: ``` 1 2 3 2 3 1 ``` 很显然我们能够按照如下步骤进行操作: ``` 1 2 3 -> 1 3 2 1 2 3 2 3 1 2 1 3 -> 1 2 3 ``` 由此可得 **$3$ 元环**有解。 下面是 $4$ 元环: ``` 1 2 3 4 -> 1 4 3 2 1 4 2 3 -"sort(a)"-> 2 3 4 1 2 1 4 3 -> 1 2 4 3 v v 1 2 3 4 1 4 3 2 2元环 NO ^ ^ ``` 它最终转变为了 $2$ 元环。 ``` 1 2 3 4 5 -> 1 5 3 4 2 1 5 3 2 4 -"sort(a)"-> 2 3 4 5 1 2 1 4 5 3 -> 1 2 4 5 3 1 2 3 4 5 -> 1 2 5 4 3 1 2 5 3 4 1 5 4 3 2 1 5 2 3 4 -> 1 2 5 3 4 YES ``` 它成功的匹配了。 事实上,我们有**结论 $1$**: 对 $k>2$ 时,任何 $k$ 元环都可以通过有限次操作得到**两个匹配的位置和一个 $k-2$ 元环**。 证明如下: 这里用一个长度为7的序列举例: ``` v v v v v v v v 1 2 3 4 5 6 7 -> 1 2 3 7 5 6 4 -> 1 2 3 5 7 6 4 -> 1 2 3 5 4 6 7 -> 1 2 3 4 5 6 7 1 2 4 5 6 7 3 1 2 4 3 6 7 5 1 2 3 4 6 7 5 1 2 3 4 5 7 6 1 2 3 4 7 5 6 ^ ^ ^ ^ ^ ^ ^ ^ ``` 我们成功的将一个 $5$ 元环变成了与原先方向相反的 $3$ 元环不过这并不影响匹配。事实上,这个结论可以类似的推广,但可能写出来会有点占地方,故供感兴趣的读者自行尝试。 根据上述结论,我们可以将所有的环通过多次操作变为不超过 $2$ 元的环,故在初始时我们**只关心每个环元数的奇偶性**,并且**奇元环能够完成匹配,偶元环最终会剩下一个不能匹配的 $2$ 元环**。 不过,我们还有一种情况: 当两个 $2$ 元环挨在一起的时候,他们可以相互交换,使得四个位置均被匹配。 ``` v v 1 2 3 4 -> 1 2 4 3 2 1 4 3 1 2 4 3 ^ ^ ``` 所以我们就能得到**结论 $2$**: **任意两个 $2$ 元环可以互相消耗**。 综合上述两个结论,我们就得到了只有一行的判断算法: **在上述“环”的定义下,如果偶元环的个数为偶数,输出`YES`,否则输出`NO`。** 然后就结束了,代码也很好写。 在实现的时候,由于每个环之间操作具有独立性,我们只需要对每个环的元数进行计算并统计即可,不需要在算法当中对 $a,b$ 数组进行任何修改操作。 # 代码 ``` #include <bits/stdc++.h> using namespace std; int t,n,cnt; int a[200005],b[200005],pos[200005],arr[200005]; bool vis[200005]; void clear(){//清空函数 for(int i=1;i<=n;i++){ pos[a[i]]=0; vis[i]=0; arr[i]=0; } } signed main(){ std::ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); cin>>t; while(t--){ cin>>n; bool flag=1; for(int i=1;i<=n;i++){ cin>>a[i]; pos[a[i]]=i; } for(int i=1;i<=n;i++){ cin>>b[i]; } for(int i=1;i<=n;i++){ if(!pos[b[i]]){ flag=0;//如果有未同时出现在a,b数组的数字,直接输出NO break; } } if(!flag){//上述情况的特判 cout<<"NO\n"; clear(); continue; } //这里直接在a,b数组上反复横跳 for(int i=1;i<=n;i++){ arr[i]=pos[b[i]]; } cnt=0;//偶环个数 for(int i=1;i<=n;i++){ if(vis[i]) continue; int siz=0,pos=i; while(!vis[pos]){ vis[pos]=1; siz++; pos=arr[pos]; } if(!(siz&1))//如果为偶环,计数器++ cnt++; } //输出结果 if(cnt&1) cout<<"NO\n"; else cout<<"YES\n"; clear();//多测清空好习惯 } return 0; } //Skadi_H ``` # 后记 审核老师辛苦了!