AtCoder Beginner Contest 293

E - Geometric Progression

https://atcoder.jp/contests/abc293/tasks/abc293_e

Time Limit: 2 sec / Memory Limit: 1024 MB

Score : 500 points

Problem Statement

Given integers A, X, and M, find i=0X1Ai, modulo M.

Constraints

  • 1A,M109
  • 1X1012
  • All values in the input are integers.

Input

The input is given from Standard Input in the following format:

A X M

Output

Print the answer.

Sample Input 1

3 4 7

Sample Output 1

5

30+31+32+33=40, which equals 5 modulo 7, so 5 should be printed.

Sample Input 2

8 10 9

Sample Output 2

0

Sample Input 3

1000000000 1000000000000 998244353

Sample Output 3

919667211

题解

典型错解——使用等比数列求和公式

我们知道等比数列求和公式:

Sn=a1qn1q1

那么易得:

i=0X1Ai=AX1A1

其中, AX 通过快速幂得到,与 A1 做除法,在取模的时候转化为乘 A1 的逆元,然后就便是典型错解。错误的原因是:并不是所有的数都存在乘法逆元。

要求 a 对于模 p 的乘法逆元 b,即 ab1(modp)

  • ap 互质

    • 可用扩展欧几里得算法得出:exgcd(a,p,b,t)=1
    • 特别的,当 p 为质数,可用费马小定理得出:ap2
  • ap 不互质

    • a 对于模 p 不存在乘法逆元

由题目可知,没有对 AM 的关系做限制,因此 A1 可能没有关于 M 的乘法逆元。

正确解法——矩阵加速递推

构造递推

an=i=0n1Ai,那么可构造递推式:

an+1=Aan+1,  a0=0

若直接进行递推,时间复杂度 O(n),由 1X1012 可知无法满足时限。

加速递推

将上述递推式改写为矩阵形式:

[an+11]=[A101][an1],  a0=0

那么我们的递推式就能变成矩阵次幂形式了:

[an1]=[A101]n[01]

同时,矩阵的次幂也是可以使用快速幂的,因为矩阵乘法也满足 M2n=(M2)n,那么我们直接用快速幂计算出:

[A101]X=[pqmn]

题目的答案便是 q.

代码

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

vector<vector<ll>> mat_mul_2d(vector<vector<ll>> a, vector<vector<ll>> b, ll p)
{
    vector<vector<ll>> ans(2, vector<ll>(2));
    ans[0][0] = ((a[0][0] * b[0][0]) % p + (a[0][1] * b[1][0]) % p) % p;
    ans[0][1] = ((a[0][0] * b[0][1]) % p + (a[0][1] * b[1][1]) % p) % p;
    ans[1][0] = ((a[1][0] * b[0][0]) % p + (a[1][1] * b[1][0]) % p) % p;
    ans[1][1] = ((a[1][0] * b[0][1]) % p + (a[1][1] * b[1][1]) % p) % p;
    return ans;
}

vector<vector<ll>> mat_fastpow_2d(vector<vector<ll>> a, ll n, ll p)
{
    vector<vector<ll>> ans(2, vector<ll>(2));
    ans[0][0] = ans[1][1] = 1;
    while (n)
    {
        if (n % 2)
            ans = mat_mul_2d(ans, a, p);
        a = mat_mul_2d(a, a, p);
        n >>= 1;
    }
    return ans;
}

void solve()
{
    ll A, X, M;
    cin >> A >> X >> M;
    vector<vector<ll>> mat = {{A, 1}, {0, 1}};
    vector<vector<ll>> ans = mat_fastpow_2d(mat, X, M);
    cout << ans[0][1] << endl;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    solve();
    return 0;
}