P5431 【模板】乘法逆元 2


原题链接

P5431
AC记录:Accepted

题目大意

给定 n n n 个正整数 a i a_i ai,求它们在模 p p p 意义下的乘法逆元。
由于输出太多不好,所以将会给定常数 k k k,你要输出的答案为:
∑ i = 1 n k i a i \sum^n_{i=1}\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,是你要求逆元的数。

输出格式

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

S a m p l e \mathbf{Sample} Sample I n p u t \mathbf{Input} Input

6 233 42
1 4 2 8 5 7

S a m p l e \mathbf{Sample} Sample O u t p u t \mathbf{Output} Output

91

H i n t & E x p l a i n \mathbf{Hint\&Explain} Hint&Explain
自己手推吧。

数据范围

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

解题思路

⚠  警告 此题时间限制仅有550ms! { \color{#FF9100}{\rule[0pt]{2pt}{40pt}} \color{#FFF4E5}{\rule[20pt]{200pt}{20pt}} \kern{-200pt} \color{#FFFFFF}{\rule[0pt]{200pt}{20pt}} \color{orange}{\raisebox{27pt}{\kern{-195pt}{\footnotesize\bf ⚠ 警告}}} \color{black}{\raisebox{7pt}{\kern{-195pt}{\footnotesize\bf 此题时间限制仅有550ms!}}} }  550ms!
出题人还算良心吧,就比乘法逆元 只多了50ms
顺便推销一下我写的乘法逆元的博客。

解法

这次我们要用 阶乘求逆元
我们设 m u l i mul_i muli 为前 i i i 个数字的乘积, i n v m u l i invmul_i invmuli m u l i mul_i muli 的逆元。
而题目里面的式子说是 ∑ i = 1 n k i a i \large\sum\limits^n_{i=1}\frac{k^i}{a_i} i=1naiki,是 k i k^i ki 1 a i \dfrac 1{a_i} ai1 也就是 a i a_i ai 的逆元的乘积。那么如何求单个元素 a i a_i ai 的乘积呢?
我们知道, i n v m u l i = 1 a 1 × a 2 × ⋯ × a i = 1 a 1 × a 2 × ⋯ × a i − 1 × 1 a i invmul_i=\dfrac 1{a_1\times a_2\times\cdots\times a_i}=\dfrac 1{a_1\times a_2\times\cdots\times a_{i-1}}\times\dfrac1{a_i} invmuli=a1×a2××ai1=a1×a2××ai11×ai1,所以我们只要消掉 1 a 1 × a 2 × ⋯ × a i − 1 \dfrac 1{a_1\times a_2\times\cdots\times a_{i-1}} a1×a2××ai11,即乘上 m u l i − 1 mul_{i-1} muli1,就可以求出 a i a_i ai 的逆元。由此可得
1 a i = i n v m u l i × m u l i − 1 \frac1{a_i}=invmul_i\times mul_{i-1} ai1=invmuli×muli1
然后就可以线性求逆元啦。
最后,还有一个优化,就是秦九韶算法,循环时,要 从大到小 循环,而每一次处理答案,只需要把答案乘 k k k 就可以了。
i n v m u l n invmul_n invmuln 可以由费马小定理求出,即 i n v m u l n = ( m u l n ) p − 2 invmul_n=(mul_n)^{p-2} invmuln=(muln)p2

上代码

#include<bits/stdc++.h>

#define endl '\n'

using namespace std;

#define int long long

int mul[5000010];
int a[5000010];
int n,p,k;

int power(int a,int b,int p)
{
    int tar=1;
    while(b)
    {
        if(b&1)
            tar=(long long)(tar*a)%p;
        a=(long long)(a*a)%p;
        b>>=1;
    }
    return tar;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    /* Code */
    cin>>n>>p>>k;
    mul[0]=1;
    for(int i=1; i<=n; i++)
        cin>>a[i],mul[i]=1ll*mul[i-1]*a[i]%p;
    int invmul=power(mul[n],p-2,p);//算invmul[n]
    int tar=0;
    for(int i=n; i>=1; i--)
    {
        int invai=1ll*invmul*mul[i-1]%p;//求ai的逆元
        tar=1ll*(tar+invai)*k%p;//秦九韶算法
        invmul=(1ll*invmul*a[i])%p;//算invmul[i-1]
    }
    cout<<tar<<endl;
    return 0;
}

完美切题 ∼ \sim

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值