公式

E - Permute K times 解説 by en_translator


This problem can be solved with a technique called doubling.

The following is references on doubling (in Japanese).

First, for \(k=0,1,\dots,59\), consider finding the following array \(P_k\):

  • \(P_{k,i} :=\) (after \(2^k\) operations, the \(i\)-th element of the resulting sequence contains the \(P_{k,i}\)-th element of the sequence before the operations)

First, \(P_0 = X\).
Furthermore, one can obtain \(P_k\) from \(P_{k-1}\) as follows.

  • \(P_{k,i} = P_{k-1}[P_{k-1,i}]\)

Intuitively, applying “\(2^{k-1}\) operations” twice mean applying “\(2^k\) operations.”

In general, for a sequence \(L\) corresponding to \(l\) operations and a sequence \(R\) corresponding to \(r\) operations, \(T_i=R[L_i]\) yields a sequence \(T\) corresponding to \(l+r\) operations. Let us call it “composition of operations.”

One can solve the original problem if we can obtain, based on these sequences \(P_i\), a sequence \(Q\) such that after \(K\) operations, the \(i\)-th element of the resulting sequence contains the \(Q_i\)-th element of the original sequence. How can we obtain it?

This can be done in the same manner as the fast exponentiation. Specifically, one can find it as follows.

  • Let \(Q=(1,2,\dots,N)\) (corresponding to \(0\) operations).
  • For \(i=0,1,\dots,59\), repeat the following.
    • If the remainder when \(K\) is divided by \(2\) is \(1\), then compose \(Q\) and \(P_i\).
    • Then, divide \(K\) by \(2\) (truncating the decimal part).

Sample code (C++):

#include<bits/stdc++.h>

using namespace std;

int main(){
  int n;
  long long k;
  cin >> n >> k;
  vector<vector<int>> p(60,vector<int>(n+1));
  for(int i=1;i<=n;i++){
    cin >> p[0][i];
  }
  vector<int> a(n+1);
  for(int i=1;i<=n;i++){
    cin >> a[i];
  }
  for(int lv=1;lv<60;lv++){
    for(int i=1;i<=n;i++){
      p[lv][i]=p[lv-1][p[lv-1][i]];
    }
  }
  vector<int> q(n+1);
  for(int i=1;i<=n;i++){q[i]=i;}
  for(int lv=0;lv<60;lv++){
    if(k%2){
      for(int i=1;i<=n;i++){
        q[i]=p[lv][q[i]];
      }
    }
    k/=2;
  }
  for(int i=1;i<=n;i++){
    if(i>=2){cout << " ";}
    cout << a[q[i]];
  }cout << "\n";
  return 0;
}

投稿日時:
最終更新: