Möhtəşəm hiylə#11320

Möhtəşəm hiylə (Həll təhlili)

Qarışdırmadan sonra soldan -ci kart a[p[q[i]]] kartı olur. Trik uğurlu olur, əgər bütün üçün aşağıdakı bərabərlik ödənirsə:

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

Permutasiyaların kompozisiyasını p \circ q ilə işarə edək: (p \circ q)[i] = p[q[i]], yəni əvvəlcə , sonra isə tətbiq olunur. ilə tərs permutasiyanı işarə edək. Onda trikin şərti belə yazıla bilər: a \circ p \circ q = I.

Bərabərliyin hər iki tərəfinə soldan əvvəlcə tərs permutasiya a^{-1}-i, sonra isə p^{-1}-i tətbiq edək:

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

Beləliklə, permutasiyası ixtiyari seçilə bilər, sonra isə birmənalı şəkildə müəyyən olunur: q[i] = p^{-1}[a^{-1}[i]]. Sabit nöqtəsi olmayan permutasiyaya deranjman deyilir. Məsələ elə bir deranjmanının tapılmasına gətirilir ki, q = p^{-1} \circ a^{-1} permutasiyası da deranjman olsun.

Təsadüfi seçim. Bütün permutasiyanı üçün yoxlamaq mümkün deyil, ona görə -ni təsadüfi seçirik:

Ən çoxu cəhd edirik. Əgər onlardan heç biri cavab vermirsə, "Impossible" çap edirik.

Niyə işləyir. Cavab yalnız kiçik üçün bəzi hallarda mövcud deyil:

n \ge 4 üçün cavab həmişə mövcuddur. Üstəlik, kiçik üçün tam axtarış göstərir ki, bütün permutasiya arasında uyğun permutasiyalarının payı ən azı -dir (minimum n = 4-də əldə olunur), və artdıqca bu pay 1/e^2 \approx 0.135-ə yaxınlaşır. Deməli, cəhddə cavab tapılmaması ehtimalı (11/12)^{1000} < 10^{-37}-dan çox deyil, bu isə praktiki olaraq sıfırdır. Əgər cavab mövcud deyilsə, heç bir cəhd uğurlu olmayacaq və proqram düzgün olaraq "Impossible" çap edəcək.

Nümunə

Dördüncü testə baxaq: a = (5, 1, 4, 2, 3).

Tərs permutasiyanı tapaq: əgər a[i] = x olarsa, onda a^{-1}[x] = i. a[1] = 5, a[2] = 1, a[3] = 4, a[4] = 2, a[5] = 3 olduğundan, a^{-1} = (2, 4, 5, 3, 1) alırıq. Şəkildə solda permutasiyası, sağda isə onun tərsi göstərilib: bütün oxlar tərsinə çevrilib.

Sabit nöqtəsi olmayan təsadüfi permutasiyası generasiya edək. Tutaq ki, p = (4, 1, 2, 5, 3). Onun tərsi p^{-1} = (2, 3, 5, 1, 4)-dir.

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

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

q = (3, 1, 4, 5, 2) alırıq. permutasiyasının sabit nöqtəsi yoxdur, ona görə cütü cavabdır. Bu, nümunədəki cavabla üst-üstə düşür.

Trikin uğurlu olduğunu yoxlayaq, yəni 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

Birinci testdə n = 2 və a = (2, 1), ona görə a^{-1} = (2, 1). Uzunluğu olan sabit nöqtəsiz yeganə permutasiya p = (2, 1)-dir, və p^{-1} = (2, 1). Onda q = p^{-1} \circ a^{-1} = (1, 2) tərs permutasiyadır, onun bütün nöqtələri sabitdir. Cavab "Impossible"-dır.

Alqoritmin realizasiyası

print funksiyası massivinin elementlərini birincidən başlayaraq çap edir.

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

is_valid funksiyası permutasiyasının sabit nöqtəsi olub-olmadığını yoxlayır.

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

Massivləri elan edirik.

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

Proqramın əsas hissəsi. Giriş məlumatlarını oxuyuruq.

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

Massivləri ilkinləşdiririk.

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

Giriş permutasiyasını oxuyuruq və dərhal onun tərsini massivində saxlayırıq. Yəni massivi a^{-1} permutasiyasını saxlayır, burada giriş permutasiyasıdır.

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

mt19937 C++-da standart psevdo-təsadüfi ədəd generatorlarından biridir (period uzunluğu olan Mersenne Twister). O, çox sürətlidir və yaxşı statistik xüsusiyyətlərə malikdir, bu da onu təsadüfi ədəd generasiyası tələb edən əksər tapşırıqlar üçün əla seçim edir.

  mt19937 gen(random_device{}());

dəyişənində cari test üçün həllin tapılıb-tapılmadığını qeyd edəcəyik.

  found = false;

təkrar yerinə yetiririk.

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

Təsadüfi permutasiya generasiya edirik. Bunun üçün massivini 0, 1, 2, ... ədədləri ilə doldururuq, sonra shuffle funksiyasından istifadə edərək birinci (sıfır olmayan) elementdən başlayaraq elementləri qarışdırırıq. Biz -dən -ə qədər olan permutasiyalarla işləyirik.

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

permutasiyasının düzgün olub-olmadığını (yəni sabit nöqtəsinin olmadığını) yoxlayırıq.

    if (!is_valid(p)) continue;

p_1 = p^{-1} permutasiyasını hesablayırıq.

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

q = p_1 ° a^{-1} = p^{-1} ° a^{-1} permutasiyasını hesablayırıq.

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

Əgər permutasiyası düzgündürsə, nəticəni çap edirik.

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

Əgər təkrardan sonra həll tapılmazsa, deməli həll mövcud deyil.

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