Amazing Trick#11320

After shuffling, the -th card from the left is the card a[p[q[i]]]. The trick succeeds if for all the following equality holds:

a[p[q[i]]] = i.

Denote by p \circ q the composition of permutations: (p \circ q)[i] = p[q[i]], that is, is applied first and then . Let be the identity permutation. Then the condition of the trick can be written as a \circ p \circ q = I.

Apply the inverse permutation a^{-1} to both sides of the equality on the left, and then p^{-1}:

p \circ q = a^{-1}, \qquad q = p^{-1} \circ a^{-1}.

Thus, the permutation can be chosen arbitrarily, and then is uniquely determined: q[i] = p^{-1}[a^{-1}[i]]. A permutation without fixed points is called a derangement. The problem reduces to finding a derangement such that the permutation q = p^{-1} \circ a^{-1} is also a derangement.

Random choice. It is impossible to iterate over all permutations , so we choose at random:

We make at most attempts. If none of them yields an answer, we print "Impossible".

Why it works. An answer does not exist only in a few cases with small :

For n \ge 4, an answer always exists. Moreover, as an exhaustive search for small shows, the fraction of suitable permutations among all permutations is at least (the minimum is reached at n = 4), and as grows it approaches 1/e^2 \approx 0.135. Therefore, the probability of not finding an answer in attempts does not exceed (11/12)^{1000} < 10^{-37}, which is practically zero. If no answer exists, none of the attempts will succeed, and the program will correctly print "Impossible".

Example

Consider the fourth test: a = (5, 1, 4, 2, 3).

Let us find the inverse permutation: if a[i] = x, then a^{-1}[x] = i. Since a[1] = 5, a[2] = 1, a[3] = 4, a[4] = 2, a[5] = 3, we get a^{-1} = (2, 4, 5, 3, 1). The figure shows the permutation on the left and its inverse on the right: all arrows are reversed.

Generate a random permutation without fixed points. Let, for example, p = (4, 1, 2, 5, 3). Its inverse is p^{-1} = (2, 3, 5, 1, 4).

Compute q[i] = p^{-1}[a^{-1}[i]]:

q[1] = p^{-1}[2] = 3, \quad q[2] = p^{-1}[4] = 1, \quad q[3] = p^{-1}[5] = 4, \\ q[4] = p^{-1}[3] = 5, \quad q[5] = p^{-1}[1] = 2

We get q = (3, 1, 4, 5, 2). The permutation has no fixed points, so the pair is the answer. It coincides with the answer from the example.

Let us check that the trick succeeds, that is, a[p[q[i]]] = i:

a[p[3]] = a[2] = 1, \quad a[p[1]] = a[4] = 2, \quad a[p[4]] = a[5] = 3, \\ a[p[5]] = a[3] = 4, \quad a[p[2]] = a[1] = 5

In the first test, n = 2 and a = (2, 1), so a^{-1} = (2, 1). The only permutation of length without fixed points is p = (2, 1), and p^{-1} = (2, 1). Then q = p^{-1} \circ a^{-1} = (1, 2) is the identity permutation, all of whose points are fixed. The answer is "Impossible".

Algorithm implementation

The print function prints the elements of the array , starting from the first one.

void print(vector<int>& a)
{
  for (int i = 1; i < a.size(); i++)
    printf("%d ", a[i]);
  printf("\n");
}

The function is_valid checks whether the permutation contains any fixed points.

bool is_valid(vector<int>& v)
{
  for (int i = 1; i < v.size(); i++)
    if (v[i] == i) return false;
  return true;
}

Declare the arrays.

vector<int> a, p, p1, q;

The main part of the program. Read the input data.

scanf("%d", &tests);
while (tests--)
{
  scanf("%d", &n);

Initialize the arrays.

  a.resize(n + 1);
  p1.resize(n + 1);
  p.resize(n + 1);
  q.resize(n + 1);

Read the input permutation and immediately store its inverse in the array . That is, the array contains the permutation a^{-1}, where is the input permutation.

  for (i = 1; i <= n; i++)
  {
    scanf("%d", &x);
    a[x] = i;
  }

mt19937 is one of the standard pseudorandom number generators in C++ (Mersenne Twister with a period of 19937). It is very fast and has good statistical properties, making it an excellent choice for most tasks requiring random number generation.

  mt19937 gen(random_device{}());

In the variable , we'll mark whether a solution is found for the current test.

  found = false;

Perform iterations.

  for (it = 0; it < 1000; it++)
  {

Generate a random permutation. To do this, populate the array with the numbers 0, 1, 2, ..., and use the shuffle function to shuffle the elements, starting from the first (non-zero) element. We work with permutations from to .

    iota(p.begin(), p.end(), 0);
    shuffle(p.begin() + 1, p.end(), gen);

Check whether the permutation is valid (i.e., it contains no fixed points).

    if (!is_valid(p)) continue;

Compute the permutation p_1 = p^{-1}.

    for (i = 1; i <= n; i++)
      p1[p[i]] = i;

Compute the permutation q = p_1 ° a^{-1} = p^{-1} ° a^{-1}.

    for (i = 1; i <= n; i++)
      q[i] = p1[a[i]];

If the permutation is valid, print the result.

    if (is_valid(q))
    {
      puts("Possible");
      print(p);
      print(q);
      found = true;
      break;
    }
  }

If no solution is found after iterations, then no solution exists.

  if (!found) puts("Impossible");
}