#include<iostream>
#define int long long
using namespace std;
const int N=3000100;
int a,n,p;
int x,y;
int inv[N];//inv[i]表示i在模p的条件下的逆元;
int fact_inv[N];//阶乘逆元,fact_inv[i]表示i的阶乘在模p的条件下的逆元.
void get_inv(int n,int p)//线性递推求连续的数的逆元,假设1~n对于p的逆元都存在
{
// 原理:设p = k*i + r 其中k=p/i,r=p%i;
// 那么: ( k*i + r )同余于0 (mod p);
// 那么: ( k*i*inv[i]*inv[r] + r*inv[i]*inv[r] )同余于0 (mod p);
// 即: ( k*inv[r] + inv[i] ) 同余于0 (mod p) ;
// 所以 inv[i] 同余于 -k * inv[r]. 其中 k =p/i(下取整) , r = p%i ;
// 所以 inv[i](通解) = -(p/i) * inv[p%i] + t*p 其中 t属于任意整数;
// 所以递推式为 : inv[i] = (-p/i * inv[p%i] % p + p)%p;
// 初始化 : inv[1]=1;
inv[1]=1;
for(int i=2;i<=n;i++) inv[i] = (-p/i * inv[p%i] % p + p)%p;
}
void get_fact_inv(int n,int p)
{
//原理:1*inv[1] 同余于 1 (mod p);
// 2*inv[2] 同余于 1 (mod p);
// ... ... ... ...
// n*inv[n] 同余于 1 (mod p);
// 所以:1*2*...*n *inv[1]*inv[2]*...*inv[n] 同余于1*1*...*1=1 (mod p);
// 所以:fact_inv[i] = inv[1]*inv[2]*...*inv[i] ;
// 所以:fact_inv[i+1] = fact_inv[i] * inv[i+1] % p;
// 初始化 : fact_inv[1] = 1;
fact_inv[0] = 1;
for(int i=1;i<=n;i++) fact_inv[i] = fact_inv[i-1] * inv[i] % p;
}
int exgcd(int a,int b)//用拓展欧几里得算法算逆元,求单个逆元时该方法最快
{
//原理 : a*x 同余于1 (mod p) <==> a*x+b*y = 1 其中 x,y为整数(可以是负数);
// 由裴蜀定理知 当且仅当 (a,b) = 1 时 ,该不定方程有解. (即逆元存在);
// 有解时 : 利用拓展欧几里得算法即可返回该不定方程的一组特解 (x y) 及 a,b的最大公约数 d;
// 那么通解为 (x y) + k * (b/d -a/d) 其中 k 为任意整数.
// 若要取逆元的最小整数解 则取 x = (x % (b/d) + b/d ) % (b/d) ;
if(!b)
{
x=1,y=0;
return a;
}
int d = exgcd(b,a%b);
int sx=x,sy=y;
x=sy;
y=sx-(a/b)*sy;
return d;
}
int qmi(int a,int p)//欧拉定理的特例==> 费马小定理
{
// 原理:若 a,p互质,则 a^phi(p) 同余于 1 (mod p) phi()为欧拉函数
// 证明:比较长,略.
// 考虑到 当 p 为质数时,phi(p) = p-1 ;
// 所以 : inv(a) = a^(phi[p]-1) = a^(p-2); (此时 p 为质数);
int res= 1 ,b = p-2;
while(b)
{
if(b&1)
{
res=res*a%p;
}
a=a*a%p;
b=b>>1;
}
return res;
}
signed main()
{
cin>>n>>p;
get_inv(n,p);
get_fact_inv(n,p);
//for(int i=1;i<=n;i++)printf("%ld\n",inv[i]);
//for(int i=1;i<=n;i++)printf("%ld\n",fact_inv[i]);
//for(int i=1;i<=n;i++)printf("%ld\n",qmi(i,p));
// for(int i=1;i<=n;i++)
// {
// int d = exgcd(i,p);
// x=(x%(p/d)+(p/d))%(p/d);
// printf("%ld\n",x);
// }
}
%%%%%%%