以下の問題の解説を理解するための知識をまとめてみました。
繰り返し二乗法
繰り返し二乗法とは、x1, x2, x4, x8, x16, ... を組み合わせて、xnを高速で計算するアルゴリズムです。
x1, x2, x4, x8, x16 は、二乗していけば、簡単に求めていくことができます。
nを1, 2, 4, 8, 16, ... の組み合わせで表すことができます。 例えば、n=7 の場合、7 = 1 + 2 + 4 で表すことができます。 もう少し表し直すと 7 = 20 + 21 + 22 となり、やっていることは2進数の変換です。
long long pow(long long x, long long n) { long long ret = 1; while (n > 0) { if (n & 1) ret *= x; // n & 1で、1ビット目を見る x *= x; // x^1, x^2, x^4, x^8, ... を計算していく n >>= 1; // nを1bit右にずらして、1bit目に各桁のbitが来るようにする } return ret; }
参考
ダブリング
繰り返し二乗法は、ダブリングの一種です。
ダンブリングは、「2k 先の要素は何か」を求めます。以下の変数を考えます。
doubling[k][i] : i番目の要素から2k先の要素は何か
初期化は、20=1個先を記録します。これをもとに2k先の要素を求めていきます。 求め方は、以下のとおりです。
doubling[k+1][i] = doubling[k][doubling[k][i]]
doubling[k][doubling[k][i]] は、「i番目の要素から2k先の要素から、さらに2k先の要素」という意味になります。
クエリとして、「K個先の要素を求める」を考えます。 Kのk桁目が1ならば now = doubling[k][now]とします。
最近共通祖先(LCA:Lowest Common Ancestor)
LCAでは、ダブリングを使います。
DFSなどで、すべての頂点について、根からの距離と親を求め(ダブリングの初期化)、ダブリングによって2k先の祖先を求めます。