传球游戏
Description
Grant老师常和小朋友们一起玩一种传球游戏。游戏是这样进行的:一群小朋友分成两组,每组n人,围成一个圈。
每一个小朋友都有一个编号(1..n之间),这个编号在其所在组中是唯一的。游戏开始之前,Grant老师会发给每个小朋友一个球,球上也有编号(1..n之间),并且一个组中的球不会有两个相同编号。然后,所有小朋友必须闭上眼睛,游戏开始。随着Grant老师口中的哨子发出的节奏,每个小朋友都用一只手把球传到右边,而用另一只手接左边的来球。突然,Grant老师的哨子停了,关键的时刻到了。小朋友马上睁开眼睛,开始与同组的小朋友之间进行传球,争取以最短的时间把球传到位。传到位是指一个组中的每一个小朋友手上的球的编号与他自己的编号相同。
最后获胜的就是那个最先把球传到位的组。如果一旦哪方出现传球失误(球没被接到而落地),或犯规(一个人手上拿两个或两个以上的球)这一组就被判输。这个游戏非常有趣,小朋友们玩了许多次。他们总结出一条经验:总是两个人之间对传。也就是说,不会出现a把球传给b,而b没有把球传给a的这种情况。这样可以避免小朋友之间的失误与犯规。不过还有个关键问题就是怎么传。究竟应该把手上的球传给谁?现在需要你编一个程序来帮助小朋友们确定传球方法。你的程序首先需要计算出从一种初始状态开始:
子问题 1:至少需要几次对传才能将球传到位。
子问题2:至少需要多少时间才能将球传到位。每一个时间单位一个小朋友可以不做任何动作,也可以与另外一个小朋友之间进行对传。注意有些对传可以同时进行。比如小朋友1与小朋友2之间的对传和小朋友3与小朋友4之间的对传就可以在一个时间单位之内完成,但是被计作两次对传。
Input
第一行是一个整数 n (2<=n<=20000)。
接下来有n 行,每行一个整数(1..n),其中第(i+1)行的整数表示i号小朋友手上的球的编号。
Output
有二行,每行一个整数,分别表示子问题1和子问题2的解。
Sample Input
4
2
1
4
3
Sample Output
2
1
对于本题,我们考虑把对换的元素放在不同的轮换中,而小朋友的之间的传球相当于乘上一个对换后的结果。
- 对换的两个元素在不同的轮换中
设两个轮换为\((a_{1},a_{2},a_{3},...,a_{n}),(b_{1},b_{2},b_{3},...,b_{n})\),乘上的对换我们设为\((a_{1},b_{1})\),那么我们就有\((a_{1},a_{2},a_{3},...,a_{n})(b_{1},b_{2},b_{3},...,b_{n})(a_{1},b_{1})=(a_{1},a_{2},a_{3},...,a_{n},b_{1},b_{2},b_{3},...,b_{n})\),可见两个轮换合并成了一个轮换 - 对换的两个元素在同一个轮换中
我们不妨设在轮换\((a_{1},a_{2},...,a_{i},a_{i+1},...,a_{n})\)中乘上一个\((a_{1},a_{i})\),那么我们有\((a_{1},a_{2},...,a_{i},a_{i+1},...,a_{n})(a_{1},a_{i})=(a_{1},a_{2},...,a_{i-1})(a_{i},a_{i+1},...,a_{n})\),可见一个轮换拆分成了两个轮换
我们所需要的状态为\((1)(2)(3)...(n)\),那么两个元素之间的轮换必定是在同一个轮换中进行的,所以只要每次传球的人都在同一个轮换内,所需要的次数都是一样的,即\((n-\)轮换个数\()\),这就是子问题1的解
那么子问题2呢?
子问题2相当于要找到最少的时间使得轮换\((a_{1},a_{2},a_{3},...,a_{n})\)变成\((a_{1})(a_{2})(a_{3})...(a_{n})\)
每步我们可以乘上若干个对换。其实,答案出乎意料的简单:
- 若轮换长度为1,则答案为0
- 若轮换长度为2,则答案为1
- 若轮换长度大于2,则答案为2
前两个结论显然成立,对于轮换长度大于2的轮换,我们有个一般的方法:
\((a_{1},a_{2},a_{3},...,a_{n})(a_{2},a_{n})=(a_{1},a_{n})(a_{2},a_{3},...,a_{n-1})\Rightarrow(a_{1},a_{n})(a_{2},a_{n-1})(a_{3},a_{4},...,a_{n-2})\Rightarrow...\)
所以我们只需要一步就需要变成一系列长度为2的轮换的乘积(可能会有长度为1的,但不影响答案),接下来也只需要一步就可以变为目标状态,子问题2便解决
/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline int read(){
int x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return x*f;
}
inline void print(int x){
if (x>=10) print(x/10);
putchar(x%10+'0');
}
const int N=2e4;
int A[N+10],size[N+10];
bool vis[N+10];
int main(){
int n=read(),tot=0,Max=0;
for (int i=1;i<=n;i++) A[i]=read();
for (int i=1;i<=n;i++){
if (vis[A[i]]) continue;
++tot;
for (int x=A[i];!vis[x];x=A[x]) size[tot]++,vis[x]=1;
Max=max(Max,size[tot]);
}
printf("%d\n%d",n-tot,Max<=2?Max-1:2);
return 0;
}