题目
题目描述
定义一个置换
P
P
P的平方
Q
Q
Q为对
[
1
,
2
,
3
,
⋯
⋯
,
n
−
1
,
n
]
[1,2,3,\cdots \cdots,n -1,n]
[1,2,3,⋯⋯,n−1,n]做两次该置换得到的排列,即
Q
i
=
P
p
i
Q_i=P_{p_i}
Qi=Ppi
现在已知一个置换的平方,求该置换
输入格式
第一行一个整数
n
n
n表示排列长度
第二行
n
n
n个整数表示所求置换的平方
输出格式
若有解则输出一行 n n n个数表示原置换(输出任意一个),否则输出 − 1 -1 −1
样例
样例输入
4
2 1 4 3
样例输出
3 4 2 1
数据范围与提示
20
%
20\%
20%的数据:
n
⩽
10
n\leqslant 10
n⩽10
100
%
100\%
100%的数据:
1
⩽
n
⩽
1
0
6
1\leqslant n\leqslant 10^6
1⩽n⩽106
题解
我们先来观察一下置换平方后是什么鬼
假设我们有一个置换:
(
2
,
3
,
1
,
5
,
4
)
(2,3,1,5,4)
(2,3,1,5,4)
它可以被拆解为两个环:
[
2
,
3
,
1
]
[
5
,
4
]
[2,3,1][5,4]
[2,3,1][5,4]
我们把它平方一下:
(
3
,
1
,
2
,
4
,
5
)
(3,1,2,4,5)
(3,1,2,4,5)
发现它可以拆成三个环:
[
3
,
1
,
2
]
[
4
]
[
5
]
[3,1,2][4][5]
[3,1,2][4][5]
我们发现,如果是一个偶环,平方后就拆开了,如果是一个奇环,就还会是一个奇环
得到了这个规律以后,我们就可以处理这道题了
先把平方后的结果拆成环,对于偶环,就两两合并,比如
[
1
,
3
,
5
]
[1,3,5]
[1,3,5]和
[
2
,
4
,
6
]
[2,4,6]
[2,4,6]合并为
[
1
,
2
,
3
,
4
,
5
,
6
]
[1,2,3,4,5,6]
[1,2,3,4,5,6];对于奇环,通过观察,可以发现,我们需要把它拆成两半之后合并,比如
[
1
,
3
,
2
]
[1,3,2]
[1,3,2]拆成
[
2
,
1
]
[
3
]
[2,1][3]
[2,1][3]合并为
[
2
,
3
,
1
]
[2,3,1]
[2,3,1](注意,这里的环指的并不是按照置换中的顺序排的,而是按遍历的顺序)
最后,对于输出
−
1
-1
−1的情况当然只有一个啦,那就是有奇数个偶环
附上代码:
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std;
int n,Size,q[1000010],v[1000010],p[1000010];
vector<int> h[1000010],l[1000010];
int main()
{
freopen("permutation.in","r",stdin);
freopen("permutation.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&q[i]);
for(int i=1;i<=n;i++){
int s=0;
for(int j=i;!v[j];j=q[j]) v[j]=1,s++,h[i].push_back(j);
l[s].push_back(i);
}
for(int i=1;i<=n;i++) if((!(i&1))&&(l[i].size()&1)){printf("-1");return 0;}
memset(v,0,sizeof(v)),Size=l[1].size();
for(int i=0;i<Size;i++) p[l[1][i]]=l[1][i];
for(int i=2;i<=n;i++){
Size=l[i].size();
for(int j=0;j<Size;j++)
if(i&1){
int x=h[l[i][j]][0],y=h[l[i][j]][i/2+1];
while(!v[x]) v[x]=v[y]=1,p[x]=y,x=q[x],p[y]=x,y=q[y];
}
else{
int x=h[l[i][j]][0],y=h[l[i][j+1]][0];
while(!v[x]&&!v[y]) v[x]=v[y]=1,p[x]=y,x=q[x],p[y]=x,y=q[y];
j++;
}
}
for(int i=1;i<=n;i++) printf("%d ",p[i]);
}