置换
Time Limit: 10 Sec Memory Limit: 256 MB
Description
定义一个置换Р的平方Q为对[1,2,3,… ,n-1,n]做两次该置换得到的排列,即 $Q_i = P_{P_i} $
现在已知一个置换的平方,求该置换。
第一行一个整数n表示排列长度。
第二行n个整数表示所求置换的平方。
Output
若有解则输出一行n个数表示原置换(输出任意一个),否则输出-1
4
2 1 4 3
Sample Output
3 4 2 1
HINT
1<=n<=10^6
Main idea
已知一个置换置换自己得到的序列,求这个置换。
Solution
显然是置换题。首先我们正向考虑,考虑一下一个置换置换自己会发生怎样的结果。
然后我们一波画图发现:如果一个轮换的长度是奇数,那么这个环所有点连边向后移一位;如果一个轮换的长度数偶数,那么就会拆解成两个长度一样的新轮换。
然后我们倒着来想,考虑如何合并。显然现在的轮换是奇数的话,我们将所有点连边向前移动一位。如果是偶数的话,再找一个长度和这个一样的轮换,把两个轮换并在一起,并在一起就是两个轮换依次取出一个。
如果轮换是偶数且找不到一对的话就显然不合法。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| #include<bits/stdc++.h> using namespace std;
const int ONE=1000005;
int n; int x,P[ONE],d[ONE],record[ONE]; int vis[ONE],cnt,tot,num; int Ans[ONE];
vector <int> q[ONE]; struct power { int len; int id; }R[ONE]; int cmp(const power &a,const power &b){return a.len < b.len;}
int get() { int res=1,Q=1;char c; while( (c=getchar())<48 || c>57 ) if(c=='-')Q=-1; res=c-48; while( (c=getchar())>=48 && c<=57 ) res=res*10+c-48; return res*Q; }
int main() { n=get(); for(int i=1;i<=n;i++) P[i]=get();
for(int i=1;i<=n;i++) { if(vis[i]) continue; cnt = 0; x = i; for(;;) { record[++cnt]=x; x = P[x]; vis[x] = 1; if(x==i) break; }
if(cnt==1) {Ans[i]=i; continue;}
if(cnt%2==1) { int len=1; num=0; for(int j=1;j<=cnt;j++) { d[len]=record[j]; len+=2; if(len>cnt) len-=cnt; } d[cnt+1]=d[1]; for(int j=1;j<=cnt;j++) Ans[d[j]]=d[j+1]; } else { R[++tot].id=tot; R[tot].len=cnt; for(int j=1;j<=cnt;j++) q[tot].push_back(record[j]); } }
if(tot%2==1) {printf("-1");exit(0);} sort(R+1,R+tot+1,cmp);
for(int j=1;j<=tot;j+=2) { int x=R[j].id, y=R[j+1].id; if(R[j].len != R[j+1].len) {printf("-1");exit(0);}
num=0; for(int i=0;i<R[j].len;i++) d[++num]=q[x][i], d[++num]=q[y][i];
d[num+1]=d[1]; for(int i=1;i<=num;i++) Ans[d[i]]=d[i+1]; }
for(int i=1;i<=n;i++) printf("%d ",Ans[i]); }
|