P5431 【模板】乘法逆元2

洛谷题目链接

刚开始做乘法逆元还是有点懵逼的~

以下式子都在模(p)意义下进行

我们把式子改一下,变成:$$sumlimits_{i=1}nki imes a_i^{-1}$$

我们先算出(a_i)的前缀积:$$s[i]=s[i-1] imes a_i$$

我们发现只要算出每一个前缀积的逆元(t_i),每一个(a_i)的逆元都好求了:$$a_i^{-1}=t_i imes s_{i-1}$$

那么怎么求每一个前缀积的逆元呢,我们可以先把$t_n运用费马小定理求出来: $$t_n=s_n^{p-2}$$

再根据:$$t_i=t_{i+1}*a_{i+1}$$

递推出所有的(t)

这样就把所有的逆元(O(n))求出来了,我们再把原式变形:$$a_1{-1}k1+a_2{-1}k2+cdots+a_{n-1}{-1}k{n-1}+a_n{-1}kn$$

[k(a_1^{-1}+k(a_2^{-1}+cdots+k(a_{n-1}^{-1}+a_n^{-1}k))) ]

我们就可以(O(n))求出答案啦~~~

接下来是美滋滋的代码时间~~~(必须用快读。。)

#include<iostream>
#include<cstdio>
#include<cmath>
#define int long long
#define N 5000007
using namespace std;
int n,p,k;
int s[N],a[N],inv_s[N];
int Read()
{
	int fu=1,ret=0;
	char c=getchar();
	while(c<'0'||c>'9')
	{
		if(c=='-')
			fu=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		ret=(ret<<3)+(ret<<1)+c-'0';
		c=getchar();
	}
	return ret*fu;
}
int qpow(int a,int b)
{
	int ans=1,res=a;
	while(b)
	{
		if(b&1)
			ans=(ans*res)%p;
		res=(res*res)%p;
		b/=2;
	}
	return ans%p;
}
signed main()
{
	n=Read(),p=Read(),k=Read();
	s[0]=1;
	for(int i=1;i<=n;++i)
	{
		a[i]=Read();
		s[i]=(s[i-1]*a[i])%p;
	}
	inv_s[n]=qpow(s[n],p-2);
	for(int i=n-1;i>=1;--i)
		inv_s[i]=(inv_s[i+1]*a[i+1])%p;
	int ans=0;
	for(int i=n;i>=1;--i)
		ans=((inv_s[i]*s[i-1])%p+ans)*k%p;
	printf("%lld
",(ans+p)%p);
	return 0;
}
原文地址:https://www.cnblogs.com/yexinqwq/p/11051316.html