AtCoder Beginner Contest 212 G - Power Pair
放置している ABC - G, Ex を解き進めたい
問題
素数 $P$ が与えられる。
- $ 0 \leq x \leq P - 1 $
- $ 0 \leq y \leq P - 1 $
- ある整数 $ n $ が存在して、$ x ^ n \equiv _ P y$
を満たす整数の組 $ (x, y) $ の個数を $ 998244353 $ で割った余りを求めよ。
前提
解法
$ x = 0 $ のとき、対応する $ y $ は $ 0 $ のみ。以降は $ x \neq 0 $ の場合を考える。
$ p $ の原始根を $ r $ とすると、ある整数 $ a, b $ を用いて $ x = r ^ a, ~ y = r ^ b $ と書ける。
$$ \begin{split} x ^ n \equiv _ P y &\iff r ^ {an} \equiv _ P r ^ b \\ &\iff an \equiv _ {P - 1} b \end{split} $$
なので、ある整数 $ n $ が存在して $ an \equiv _ {P - 1} b $ を満たすような $ (a, b) $ を数えればよい。
$ \mod P - 1 $ 上で $ \Brace{ka \mid k \in \mathbb{Z}} = \Brace{k\gcd(a, P - 1) \mid k \in \mathbb{Z}} $ が成り立つので、$ a $ を固定したとき、対応する $ b $ は $ \frac{P - 1}{\gcd(a, P - 1)} $ 個存在する。
$ \gcd(a, P - 1) $ ごとにまとめて数えることにすると、答えは
$$ \sum_{g \mid P - 1} \frac{P - 1}{g} \times \# \Brace{x \in \mathbb{Z}/(P-1)\mathbb{Z} \mid g = \gcd(x, P - 1)} $$
となる。
あとは $$ f(g) = \# \Brace{x \in \mathbb{Z}/(P-1)\mathbb{Z} \mid g = \gcd(x, P - 1)} $$ を求めれば終わり。 $$ \# \Brace{x \in \mathbb{Z}/(P-1)\mathbb{Z} \mid g \mid \gcd(x, P - 1)} = \sum_{g \mid h} f(h) $$ は簡単に計算できる($ g $ の倍数の個数に等しい)ので、低速メビウス変換*1をすれば OK。
コード
https://atcoder.jp/contests/abc212/submissions/38687433
int main() { lint p; scanf("%lld", &p); vector<lint> ds; for (lint i = 1; i * i <= p - 1; ++i) { if ((p - 1) % i == 0) { ds.push_back(i); if (i * i != p - 1) { ds.push_back((p - 1) / i); } } } sort(ds.begin(), ds.end()); mint dp[ds.size()]; rrep(i, ds.size()) { dp[i] += (p - 1) / ds[i]; rep(j, i) { if (ds[i] % ds[j] == 0) { dp[j] -= dp[i]; } } } mint ans = 1; rep(i, ds.size()) { ans += dp[i] * ((p - 1) / ds[i]); } printf("%u\n", ans.val()); }
関連
原始根は乱択で高速に求まる
- Motwani & Raghavan の Randomized Algorithms に記述がある
- 原始根のアルゴリズム – 37zigenのHP にも詳しく書いてある
高速メビウス変換の約数版(今回は使わない)
原始根を使う問題
約数包除を使う問題
*1:造語なので注意