前置芝士
exgcd 就是用来求解 这个方程的最小整数解的()。
拿到这个柿子,第一步当然是推导、化简啦。
推导过程
首先为了方便推导,令 ;
很明显,若 ,则 ;
对于 的情况:
设 为 的最小整数解;
而我们想要令 最小,所以 。
模板代码
int exgcd(int a,int b,int &x,int &y) // x 和 y 是引用,因为我懒得写结构体…… 
{
	if(a<b) // 如果 a < b 那么交换 a 和 b 来求 
	{
		return exgcd(b,a,y,x);
	}
	if(b==0) // 如果 b 为 0,那么 gcd(a,b) = a,x = 1,y = 0 
	{
		x=1;
		y=0;
		return a;
	}
	// 其它情况 
	int tmpx,tmpy;
	int res=exgcd(b,a%b,tmpx,tmpy);
	x=tmpy;
	y=tmpx-a/b*tmpy;
	return res;
}
应用(求乘法逆元)
可以干很多事情,甚至还有 ……(扩展扩展欧几里得函数)
所以理解好它很重要 awa
的一个比较常见的用途是求乘法逆元。
设 在 意义下的逆元是 ,那么
即
设 为 ,那么
即
这个方程就是 的形式,所以我们可以得知** 的逆元存在的必要条件是 和模数 互质**。
所以我们只需要使用 就可以求出逆元了。
求逆元代码如下:(P5431 【模板】乘法逆元 2)
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n;
long long p,k;
long long a[5000005],sum[2][5000005];
inline long long read()
{
	long long s=0,w=1,ch=getchar();
	while(ch<'0'||ch>'9') ch=='-'?w=-1,ch=getchar():ch=getchar();
	while(ch>='0'&&ch<='9') s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
	return s*w;
}
long long exgcd(long long a,long long b,long long &x,long long &y) // x 和 y 是引用,因为我懒得写结构体…… 
{
	if(a<b) // 如果 a < b 那么交换 a 和 b 来求 
	{
		return exgcd(b,a,y,x);
	}
	if(b==0) // 如果 b 为 0,那么 gcd(a,b) = a,x = 1,y = 0 
	{
		x=1;
		y=0;
		return a;
	}
	// 其它情况 
	long long tmpx,tmpy;
	long long res=exgcd(b,a%b,tmpx,tmpy);
	x=tmpy;
	y=tmpx-a/b*tmpy;
	return res;
}
inline long long inv(long long val) // exgcd 求乘法逆元 
{
	long long x,y;
	if(exgcd(val,p,x,y)!=1)
	{
		return -1;
	}
	return (x%p+p)%p;
}
int main()
{
	scanf("%d%lld%lld",&n,&p,&k);
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
	}
	sum[0][0]=1;
	sum[1][n+1]=1;
	for(int i=1;i<=n;i++)
	{
		sum[0][i]=sum[0][i-1]*a[i]%p;
	}
	for(int i=n;i>=1;i--)
	{
		sum[1][i]=sum[1][i+1]*a[i]%p;
	}
	long long summ=0,base=1;
	for(int i=1;i<=n;i++)
	{
		base=base*k%p;
		summ+=base*sum[0][i-1]%p*sum[1][i+1]%p;
		summ%=p;
	}
	printf("%lld\n",summ*inv(sum[0][n])%p);
	return 0;
}
