中国剰余定理のことをあれこれ調べていたら勢いで解いたん
問題へのリンク
問題概要
整数 が与えられる。
mod. )
が成立するような 以上 以下の整数 を数え上げよ。
制約
- は素数
- <
解法
Fermat の小定理から以下のことが言える:
- は mod. において周期 である
- は mod. において周期 である
したがって、 と は互いに素だから、 mod. は全体として周期 となることがわかる。
ここで、 の方を固定して集計することを考える。すなわち、 (mod. ) の場合について数えて条件を満たす の個数を数え上げて、それを について合算する。
(mod. )
⇔ (mod. )
となることから、すなわち
- (mod. )
- (mod. )
を満たす を数え上げることになる。中国剰余定理により、
(mod. )
を満たすような が求められるので、それを用いて 以下の整数を数え上げる。
#include <iostream> #include <vector> #include <cmath> using namespace std; inline long long mod(long long a, long long m) { return (a % m + m) % m; } long long inv(long long a, long long m) { long long b = m, u = 1, v = 0; while (b) { long long t = a/b; a -= t*b; swap(a, b); u -= t*v; swap(u, v); } return mod(u, m); } long long gcd(long long a, long long b) { if (b == 0) return a; else return gcd(b, a % b); } pair<long long, long long> ChineseRem(long long b1, long long m1, long long b2, long long m2) { long long x = 0, m = 1; long long a = m, b = b1 - x, d = gcd(m1, a); if (b % d != 0) return make_pair(0, -1); long long t = mod(b / d * inv(a / d, m1 / d), m1 / d); x += m * t; m *= m1 / d; a = m, b = b2 - x, d = gcd(m2, a); if (b % d != 0) return make_pair(0, -1); t = mod(b / d * inv(a / d, m2 / d), m2 / d); x += m * t; m *= m2 / d; return make_pair(x % m, m); } long long a, b, p, x; int main() { cin >> a >> b >> p >> x; long long res = 0; long long pow = 1; for (long long k = 0; k < p-1; ++k) { // ≡ k(mod. p-1), ≡ a^{-k}b (mod. p) long long b2 = inv(pow, p) * b % p; pair<long long, long long> c = ChineseRem(k, p-1, b2, p); long long tmp = (x + (c.second - c.first) % c.second) / c.second; res += tmp; pow = pow * a % p; } cout << res << endl; }