1 条题解
-
0
自动搬运
来自洛谷,原作者为

破壁人五号
「不过回归空白 空白的原来」| AFO | ICPCer | wallbreaker5th.top搬运于
2025-08-24 22:36:57,当前版本为作者最后更新于2022-03-16 14:01:33,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
这题本人曾经在去年 6 月搬到过联考,而且是在最小化 的前提下最小化 的加强版。

首先特判所有数已经归位的情况,这时 。其余情况下都有 ( 的话你把 交换到前 个位置之后就回不去了)。
以下的「Solution 1」为本题做法(你可以在 Matrix 67 的博客 里面找到本题),「Solution 2」为最小化 的做法。代码是最小化了 的。
该问题最早出现于 Futurama 第 6 季第 10 集 The Prisoner of Benda。(虽然出题人并没有看过)
下面将交换 的操作记为 。记 。
Solution 1
原动画中给出了如下的构造解:
将排列 分解为多个非空的环之积,考虑分别处理每个环。不失一般性地,我们考虑 ,对此进行操作:$(1x)\to(2y)\to(3y)\to(4y)\cdots\to(ny)\to(2x)\to(1y)$。在处理完每个环之后, 的位置可能互换,此时我们需要再做 。
设 为 个非空的环之积,环长之和为 ,则其需要 次操作。这在环数大于 时不是最优解。期望得分 分。
Solution 2
这篇论文 给出了最优构造(如果你想要阅读原论文的话,注意此处的操作是自左向右执行的,而论文中是从右向左执行的):
考虑将原排列分解为多个非空的环 之积,每个环形如:
$$\begin{pmatrix} a_1,a_2,a_3,\cdots a_k\\ a_k,a_1,a_2,\cdots a_{k-1} \end{pmatrix} $$以 为例,我们定义:
进行操作:
$$\begin{aligned} &F_r(y)\to F_{r-1}(y)\to \cdots\to F_{2}(y)\\ \to & \left[(a_1x)\to G_1(y)\to(a_k x)\right] \\ \to &G_2(x)\to G_3(x)\to \cdots \to G_r(x) \end{aligned}$$设 为 个非空的环之积,环长之和为 ,则其需要 次操作。
其正确性容易验证。我们下面证明这个解是最优的。
证明
首先我们只保留环中的元素,那些不用换位置的元素我们忽略。
假设 的解不是最优,那么操作数 。通过考虑置换的奇偶性(即逆序对个数的奇偶性)我们可以发现 ,于是我们有 。
另一方面,对于每个元素,它的初始位置与最终位置不同,一定至少被交换过一次。因此 。
进一步地,考虑第一个环 $C_1=\begin{pmatrix} a_1,a_2,a_3,\cdots a_k\\ a_k,a_1,a_2,\cdots a_{k-1}\end{pmatrix}$,我们考虑环中最后一次被操作到的元素,它一定是与 或 交换,且把位于这个元素处的、不属于 的东西扔出去;在这个交换之前,它一定与 中的一个交换过一次。其余环类似。于是对于每个环,它至少会带来一次额外的操作,于是我们有 。
综上,,且不可能出现操作 。
类似地,对于每个环,我们考虑环中第一次被操作到的元素,它同样会被交换了两次。于是对于每个环,它首次操作到的元素和最后一次被操作到的元素是同一个。
总结一下,对于环 ,它有以下性质:
- 有且仅有唯一一个元素 ,满足有两次关于 的操作;
- 对于其他所有 中的元素,其操作时间在 的这两次操作之间。
其余环也有类似性质。
现在对于每个环 ,定义 为对其首次操作与最后一次操作之间(不含)的操作数。
不失一般性地,我们:
- 通过给环编号,可以使得 ;
- 通过交换 ,可以使得 在 之前;
- 通过改变第一个环的起点,可以使得 为 的最后一个元素()。
现在考虑 与 之间(含)的所有包含 的操作,记作 。下面我们用反证法证明 一定包含 以外的元素。
假设 中只包含 中的元素。由于我们需要对于 把 移到 ,以下操作必须按照如下顺序执行:
此时 到达不了 ,矛盾。
现在假设 ,而 为 中紧接在 之前的操作。显然我们的操作不能把 最终送到 , 应当在 之间出现两次,于是 ,矛盾。证毕。
#include<bits/stdc++.h> using namespace std; const int N=3e5+10; int getint(){ int x;scanf("%d",&x);return x; } int a[N]; bool vis[N]; vector<int>r[N];int cnt=0; vector<pair<int,int> >ans; void swp(int x,int y){ ans.emplace_back(min(x,y),max(x,y)); swap(a[x],a[y]); } int main(){ int n=getint(); for(int i=1;i<=n;i++)a[i]=getint(); a[n+1]=n+1;a[n+2]=n+2; for(int i=1;i<=n;i++){ if(!vis[i]){ int u=i; while(!vis[u]){ vis[u]=1; r[cnt].push_back(u); u=a[u]; } // reverse(r[cnt].begin(),r[cnt].end()); ++cnt; } } vector<int>cy; for(int i=0;i<cnt;i++)if(r[i].size()>1)cy.push_back(i); if(cy.empty())return puts("0 0"),0; for(int i=cy.size()-1;i;--i)swp(r[cy[i]].back(),n+2); swp(r[cy[0]].back(),n+1); for(int i:r[cy[0]])swp(i,n+2); swp(r[cy[0]].front(),n+1); for(int i=1;i<cy.size();i++)for(int j:r[cy[i]])swp(j,n+1); swp(n+1,n+2); printf("%d %d\n",2,(int)ans.size()); for(auto i:ans) printf("%d %d\n",i.first,i.second); // for(int i=1;i<=n+2;i++)cerr<<"> "<<a[i];cerr<<endl; return 0; }
- 1
信息
- ID
- 7565
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者