威尔逊定理: p为素数,则
p
∣
(
p
−
1
)
!
+
1
p\mid(p-1)!+1
p∣(p−1)!+1,则
(
p
−
2
)
!
≡
1
(
m
o
d
p
)
(p-2)!\equiv1(mod\ p)
(p−2)!≡1(mod p)
欧拉定理: p,a互素,则
a
ϕ
(
p
)
≡
1
(
m
o
d
p
)
a^{\phi(p)}\equiv1(mod\ p)
aϕ(p)≡1(mod p)
费马小定理: p为素数,则
a
p
≡
a
(
m
o
d
p
)
a^p\equiv a(mod\ p)
ap≡a(mod p)
推论:p为素数,当
p
∣
a
,
a
p
−
1
≡
0
(
m
o
d
p
)
p\mid a,a^{p-1}\equiv0(mod\ p)
p∣a,ap−1≡0(mod p);当
p
∤
a
,
a
p
−
1
≡
1
(
m
o
d
p
)
p\nmid a,a^{p-1}\equiv 1(mod\ p)
p∤a,ap−1≡1(mod p)
欧几里得算法
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
int lcm(int a,int b){
return a/gcd(a,b)*b;
}
扩展欧几里得算法
ll exgcd(ll a,ll b,ll& x,ll& y){
if(!b){
x=1;
y=0;
return a;
}
ll d=exgcd(b,a%b,y,x);
y-=x*(a/b);
return d;
}
欧拉筛
const int maxn=1000;
bool number[maxn+5];
int prime[maxn+5];
void euler_sieve(){
int i,j,cnt=0;
memset(number,true,sizeof(number));
memset(prime,0,sizeof(prime));
for(int i=2;i<=maxn;i++){
if(number[i]) prime[cnt++]=i;
for(j=0;j<cnt&&prime[j]*i<=maxn;j++){
number[prime[j]*i]=false;
if(i%prime[j]==0) break;
}
}
}
快速幂
ll mod_exp(ll a,ll b,ll n){
ll res=1;
while(b){
if(b&1)res=mod_mul(res,a,n);
a=mod_mul(a,a,n);
b>>=1;
}
return res;
}
miller-rabin判断大素数
ll mod_mul(ll a,ll b,ll n){
ll res=0;
while(b){
if(b&1)res=(res+a)%n;
a=(a+a)%n;
b>>=1;
}
return res;
}
ll mod_exp(ll a,ll b,ll n){
ll res=1;
while(b){
if(b&1)res=mod_mul(res,a,n);
a=mod_mul(a,a,n);
b>>=1;
}
return res;
}
bool miller_rabin(ll n){
if(n==2||n==3||n==5||n==7||n==11)return true;
if(n==1||!(n%2)||!(n%3)||!(n%5)||!(n%7)||!(n%11))return false;
ll x,pre,u;
int i,j,k=0;
u=n-1;
while(!(u&1)){
k++;u>>=1;
}
srand((ll)time(0));
for(i=0;i<5;i++){//5次足够AC了
x=rand()%(n-2)+2;
if((x%n)==0)continue;
x=mod_exp(x,u,n);
pre=x;
for(j=0;j<k;j++){
x=mod_mul(x,x,n);
if(x==1&&pre!=1&&pre!=n-1)return false;
pre=x;
}
if(x!=1)return false;
}
return true;
}
求逆元
x
y
≡
1
(
m
o
d
p
)
xy\equiv1(modp)
xy≡1(modp),则称xy互为逆元
除a取模等价于乘a逆元取模
// 扩展欧几里得法,通用
ll get_inv(ll a,ll mod){
ll x,y;
ll d=exgcd(a,mod,x,y);
return d==1?(x%mod+mod)%mod:-1;
}
//费马小定理法,仅当mod为素数
ll prime_inv(ll a,ll mod){
return mod_exp(a,mod-2,mod);
}
//线性法,用来求一组逆元
void linear_inv(int inv[],int len,int mod){
inv[0]=inv[1]=1;
for(int i=2;i<len;i++){
inv[i]=((mod-mod/i)*inv[mod%i])%mod;
}
}
中国剩余定理
ll china(ll a[],ll b[],int n)//a[]为除数,b[]为余数
{
ll M=1,y,x=0;
for(int i=0;i<n;++i)
M*=a[i];
for(int i=0;i<n;++i)
{
ll w=M/a[i];
ll tx=0;
int t=exgcd(w,a[i],tx,y);
x=(x+w*(b[i]/t)*x)%M;
}
return (x+M)%M;
}
组合数之打表法,N<=3000
//组合数打表模板,适用于N<=3000
//c[i][j]表示从i个中选j个的选法。
const int N=33;
long long C[N][N];
void get_C(int maxn)
{
C[0][0] = 1;
for(int i=1;i<=maxn;i++)
{
C[i][0] = 1;
for(int j=1;j<=i;j++)
C[i][j] = C[i-1][j]+C[i-1][j-1];
//C[i][j] = (C[i-1][j]+C[i-1][j-1])%MOD;
}
}
组合数之求单个值 N<=2e5
#include <iostream>
#include <bits/stdc++.h>
#define maxn 200005
typedef long long ll;
using namespace std;
const ll mod=998244353;
ll fac[maxn],inv[maxn];
ll pow_mod(ll a,ll n)
{
ll ret =1;
while(n)
{
if(n&1) ret=ret*a%mod;
a=a*a%mod;
n>>=1;
}
return ret;
}
void init()
{
fac[0]=1;
for(int i=1;i<maxn;i++)
{
fac[i]=fac[i-1]*i%mod;
}
}
ll Cc(ll x, ll y)
{
return fac[x]*pow_mod(fac[y]*fac[x-y]%mod,mod-2)%mod;
}
int main(){
ll n,m;
init();
while(1){
cin>>n>>m;
cout<<Cc(n,m)<<endl;
}
}