Codeforces 612E - Square Root of Permutation

E. Square Root of Permutation

A permutation of length n is an array containing each integer from 1 to n exactly once. For example, q = [4, 5, 1, 2, 3] is a permutation. For the permutation q the square of permutation is the permutation p that p[i] = q[q[i]] for each i = 1… n. For example, the square of q = [4, 5, 1, 2, 3] is p = q^2 = [2, 3, 4, 5, 1].

This problem is about the inverse operation: given the permutation p you task is to find such permutation q that q^2 = p. If there are several such q find any of them.
Input

The first line contains integer n (1 ≤ n ≤ 106) — the number of elements in permutation p.

The second line contains n distinct integers p1, p2, …, pn (1 ≤ pi ≤ n) — the elements of permutation p.
Output

If there is no permutation q such that q2 = p print the number “-1”.

If the answer exists print it. The only line should contain n different integers qi (1 ≤ qi ≤ n) — the elements of the permutation q. If there are several solutions print any of them.
Sample test(s)
Input

4
2 1 4 3

Output

3 4 2 1

Input

4
2 1 3 4

Output

-1

Input

5
2 3 4 5 1

Output

4 5 1 2 3

数组表示的就是映射关系 如a[1] = 2;就表示为1 -> 2;但是题目给定是i -> (a[i]) -> a[j] (a[j] = a[a[i]]);现在我们知道i和a[j]要求的是a[i];

置换的预备知识:
(a1,a2,a3)表示a1->a2,a2->a3,a3->a1;由于映射为一一对应关系,具有可逆性;(求解的依据)
对于一个置换(a1…an)一定可以划分为若干个不相交的循环
如(2,1,4,3) 变成置换之后为(1,2)(3,4)
特别注意循环乘法之间拆合关系(逆推原始的循环)以及元素位置的改变(得到答案): ans[位置] = val;

对于循环里面的元素个数为奇数时,(a[1],a[2],a[3])(a[1],a[2],a[3]) = (a[1],a[3],a[2]);即 a[1]->a[2]->a[3] ==>a[1]->a[3];同理a[2]->a[3]->a[1] ==>a[2]->a[1]; a[3] ->a[2];扩展到size = 2k+1个数的循环相乘呢?很容易知道当下标从0开始时;每个数的位置变成了2*i%size;同时告诉我们,循环的大小为奇数时,可以由自己生成,所以不用去管奇数的是否配对;

为偶数时:如(a1,a2,a3,a4)^2 = (a1,a3)(a2,a4);这就告诉我们,当p中分解出来的一个循环的大小为偶数时,循环只能由一个2*n的循环分裂得到(那我们在你想得到答案时就需要两个2k大小的循环合并成一个大小为4k的循环),在这时判断是否无解;偶数大小的循环平方时个数二分;而奇数只是把同奇偶(以下标看奇偶)的合并在一起了;

在编码时,开始就是用了循环分解算法,分解成cnt个循环节;可能会怀疑为什么这就是在一个循环里面呢?依上面的置换乘积可以看出循环大小为奇数时,只是调换了元素的对应关系,但是并没有改变ai的值,所以我们只需按照这个调换关系,逆着推回去即可;(奇偶调换两次就等于没调换~~),如果是偶数,那么分裂后还在一个循环中的元素,之前一定在同一个循环中,所以只需能配对就可以得到配对的两个的父循环~~(逆推的原理,也是理解的关键)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e6+10;
int B[MAXN],vis[MAXN],ans[MAXN],id[MAXN];
vector<int> v[MAXN];
#define pb push_back
bool cmp(const vector<int> &a,const vector<int> &b)
{
    return a.size() < b.size();
}
int main()
{
    int i,n,cnt = 0,tot = 0;
    cin>>n;
    for(i = 1;i <= n;i++)
        scanf("%d",B+i);
    for(i = 1;i <= n;i++)if(vis[i] != 1){
        int tmp = i;
        ++cnt;
        do{
            vis[tmp] = 1;
            v[cnt].pb(tmp);
            tmp = B[tmp];
        }while(tmp != i);
    }
    sort(v+1,v+1+cnt,cmp);
    for(i = 1;i <= cnt;i++){
        int sz = v[i].size();
        if(sz & 1){
            for(int k = 0;k < sz;k++)
                id[k*2%sz] = v[i][k];
            for(int k = 0;k < sz;k++)
                ans[id[k]] = id[(k+1)%sz];
        }
        else if(sz == v[i+1].size()){   
            for(int k = 0;k < sz;k++){
                ans[v[i][k]] = v[i+1][k];
                ans[v[i+1][k]] = v[i][(k+1)%sz];
            }
            i++;
        }
        else{
            return puts("-1"),0;
        }
    }
    for(i = 1;i <= n;i++){
        printf("%d ",ans[i]);
    }
}
View Code

 

 
    -
posted @ 2016-01-21 17:07  hxer  阅读(441)  评论(0编辑  收藏  举报