Official

E - Geometric Progression Editorial by cn449


数列 (an)(a_n)an=i=0n1Aia_n = \displaystyle\sum_{i=0}^{n-1} A^i で定めると、a0=0a_0 = 0an+1=Aan+1a_{n+1} = Aa_n + 1 を満たします。求めるべきものは aXmodMa_X \bmod M です。

ここで、上の漸化式より行列を用いて (an+11)=(A101)(an1)\begin{pmatrix} a_{n+1} \\ 1 \end{pmatrix} = \begin{pmatrix} A & 1 \\ 0 & 1 \end{pmatrix} \begin{pmatrix} a_n \\ 1 \end{pmatrix} と表すことができます。

a0=0a_0 = 0 であったため、(aX1)=(A101)X(01)\begin{pmatrix} a_X \\ 1 \end{pmatrix} = \begin{pmatrix} A & 1 \\ 0 & 1 \end{pmatrix}^X \begin{pmatrix} 0 \\ 1 \end{pmatrix} です。

(A101)X\begin{pmatrix} A & 1 \\ 0 & 1 \end{pmatrix}^X の各成分を MM で割った余りは繰り返し二乗法を用いて O(logX)O(\log X) で求めることができます(整数の XX 乗を MM で割った余りを求めるのと同様の要領です)。

よってこの問題を O(logX)O(\log X) で解くことができました。

なお、i=0X1Ai=AX1A1\displaystyle\sum_{i=0}^{X-1} A^i = \frac{A^X-1}{A-1} ですが、これを利用して解こうとする場合 mod M\text{mod } M における A1A-1 の逆元が存在するとは限らないことに注意してください。

実装例

Copy
  1. #include <iostream>
  2. #include <vector>
  3. using namespace std;
  4. using ll = long long;
  5. vector<vector<ll>> mat_mul(vector<vector<ll>> a, vector<vector<ll>> b, ll mod) {
  6. // 行列乗算
  7. int n = a.size();
  8. vector<vector<ll>> res(n, vector<ll>(n));
  9. for (int i = 0; i < n; i++) {
  10. for (int j = 0; j < n; j++) {
  11. for (int k = 0; k < n; k++) {
  12. res[i][j] += a[i][k] * b[k][j];
  13. res[i][j] %= mod;
  14. }
  15. }
  16. }
  17. return res;
  18. }
  19. vector<vector<ll>> mat_pow(vector<vector<ll>> a, ll b, ll mod) {
  20. // 行列累乗
  21. int n = a.size();
  22. vector<vector<ll>> res(n, vector<ll>(n));
  23. for (int i = 0; i < n; i++) res[i][i] = 1;
  24. while (b) {
  25. if (b & 1) res = mat_mul(res, a, mod);
  26. a = mat_mul(a, a, mod);
  27. b >>= 1;
  28. }
  29. return res;
  30. }
  31. int main() {
  32. ll a, x, m;
  33. cin >> a >> x >> m;
  34. vector<vector<ll>> f = { {a,1},{0,1} };
  35. vector<vector<ll>> g = mat_pow(f, x, m);
  36. cout << g[0][1] << '\n';
  37. }
#include <iostream>
#include <vector>
using namespace std;
using ll = long long;

vector<vector<ll>> mat_mul(vector<vector<ll>> a, vector<vector<ll>> b, ll mod) {
	// 行列乗算
	int n = a.size();
	vector<vector<ll>> res(n, vector<ll>(n));
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			for (int k = 0; k < n; k++) {
				res[i][j] += a[i][k] * b[k][j];
				res[i][j] %= mod;
			}
		}
	}
	return res;
}

vector<vector<ll>> mat_pow(vector<vector<ll>> a, ll b, ll mod) {
	// 行列累乗
	int n = a.size();
	vector<vector<ll>> res(n, vector<ll>(n));
	for (int i = 0; i < n; i++) res[i][i] = 1;
	while (b) {
		if (b & 1) res = mat_mul(res, a, mod);
		a = mat_mul(a, a, mod);
		b >>= 1;
	}
	return res;
}

int main() {
	ll a, x, m;
	cin >> a >> x >> m;
	vector<vector<ll>> f = { {a,1},{0,1} };
	vector<vector<ll>> g = mat_pow(f, x, m);
	cout << g[0][1] << '\n';
}

posted:
last update:



2025-04-05 (Sat)
15:58:31 +00:00