P5431 【模板】乘法逆元 2

【模板】乘法逆元 2

题目描述

给定 n n n 个正整数 a i a_i ai ,求它们在模 p p p 意义下的乘法逆元。

由于输出太多不好,所以将会给定常数 k k k,你要输出的答案为:
∑ i = 1 n k i a i \sum\limits_{i=1}^n\frac{k^i}{a_i} i=1naiki

答案对 p p p 取模。

输入格式

第一行三个正整数 n , p , k n,p,k n,p,k,意义如题目描述。
第二行 n n n 个正整数 a i a_i ai,是你要求逆元的数。

输出格式

输出一行一个整数,表示答案。

样例 #1

样例输入 #1

6 233 42
1 4 2 8 5 7

样例输出 #1

91

提示

对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 1 0 5 1\le n \le 10^5 1n105

对于 100 % 100\% 100% 数据, 1 ≤ n ≤ 5 × 1 0 6 1\le n \le 5\times 10^6 1n5×106 2 ≤ k < p ≤ 1 0 9 2\le k < p \le 10^9 2k<p109 1 ≤ a i < p 1\le a_i < p 1ai<p,保证 p p p 为质数。

AC题解—快速幂+逆元:

// 线性求任意 n 个数的逆元,这个题目就是他的一个变种
#include <iostream>
using namespace std;
typedef long long ll;
ll n, p, k;
ll qpow(ll a, ll b)
{
    a %= p;
    ll res = 1;
    while (b > 0)
    {
        if (b & 1)
        {
            res = res * a % p;
        }
        a = a * a % p;
        b >>= 1;
    }
    return res;
}
inline int read()
{
    int f = 1, x = 0;
    char ch;
    do
    {
        ch = getchar();
        if (ch == '-')
            f = -1;
    } while (ch < '0' || ch > '9');
    do
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    } while (ch >= '0' && ch <= '9');
    return f * x;
}
int main()
{
    n = read();
    p = read();
    k = read();
    ll ans = 0;
    ll temp = k;
    ll a[n + 5], s[n + 5], sv[n + 5]; // inv[n + 5](可以不存); // a[i]给定的数 s[i]表示i个数的前缀积 sv[i]表示s[i]的逆元  inv[i]表示a[i]的逆元
    for (int i = 1; i <= n; i++)
    {
        a[i] = read();
    }
    s[0] = 1;
    for (int i = 1; i <= n; ++i)
        s[i] = s[i - 1] * a[i] % p; // 求前缀积
    sv[n] = qpow(s[n], p - 2);      // 前缀积的逆元
    for (int i = n; i >= 1; --i)
        sv[i - 1] = sv[i] * a[i] % p;
    // 因为 sv_n 是 n 个数的积的逆元,所以当我们把它乘上 a_n 时,就会和 a_n 的逆元抵消,于是就得到了 a_1 到 a_{n-1} 的积逆元,记为 sv_{n-1}。
    // 同理我们可以依次计算出所有的 sv[i]
    for (int i = 1; i <= n; ++i)
    {
        // inv[i] = sv[i] * s[i - 1] % p;
        // a[i]^(-1)=sv[i]*s[i-1]--->前i个数的逆元*前i-1个数的积
        // ans = (ans + (ll)inv[i] * temp) % p;
        ans = (ans + (ll)(sv[i] * s[i - 1] % p) * temp) % p;
        temp = (temp * k) % p;
    }
    printf("%lld", ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值