競技プログラミング日記

主に AtCoder の記事です

アルゴリズムと数学062

アルゴリズムと数学062 - Teleporter

解法0: 周期性を用いる

\(i \rightarrow a[i]\) と遷移を繰り返すと,あるところでサイクルに入る. \(i_{0}\)からスタートするとして, \[ i_{0} \rightarrow \cdots a \rightarrow b \rightarrow \cdots \rightarrow c \rightarrow a \rightarrow \cdots \] の形となる. サイクルの部分の長さを調べれば,そのサイクル部分を減らせるので, 最悪で\(O(N)\)のシュミレーションで解ける.

解法1: ダブリング

こちらはテンプレ通りなので機械的にできる. \(2^{i}, i \in \mathbb{N}\) 回移動したときの 移動先を前計算しておけば, 指数を2進数展開すれば \(O(log E)\), \(E\)は指数 で求まる.

使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"

// cycle
int main() {
  ll n, k;
  cin >> n >> k;
  vll a(n); rep(i,n) { cin >> a[i]; a[i]--; }

  vll ord(n, -1);
  ll cyc_len, cyc_v;
  {
    ll i = 0;
    ll cnt = 0;
    while(ord[i]==-1){
      ord[i] = cnt++;
      i = a[i];
    }
    cyc_len = cnt-ord[i];
    cyc_v = i;
  }

  ll r;
  {
    ll cnt = 0;
    ll i = 0;
    while(i != cyc_v){
      cnt++;
      i = a[i];
    }
    r = cnt;
  }

  {
    if(k >= r) k = r + (k-r)%cyc_len;
    ll i = 0;
    rep(_,k){
      i = a[i];
    }
    cout << i+1 << endl;
  }

  return 0;
}

// doubling
int main() {
  ll n,k;
  cin >> n >> k;
  vll a(n); rep(i,n) { cin >> a[i]; a[i]--; }

  vvll dp(63, vll(n));
  rep(i,n) dp[0][i] = a[i];
  rep(j,62) rep(i,n) dp[j+1][i] = dp[j][ dp[j][i] ];

  ll j = 0;
  ll i = 0;
  while(k){
    if(k&1){
      i = dp[j][i];
    }
    k >>= 1;
    j++;
  }
  cout << i+1 << endl;

  return 0;
}