扩展卢卡斯学习笔记

扩展卢卡斯学习笔记

用途

\({\mathrm{C}}_n^m \bmod{p}\)

其中 \(p\) 不一定是质数。

求法

首先将 \(p\) 进行质因数分解,分解为 \(p_1^{a_1}p_2^{a_2} \cdots p_k^{a_k}\) 的形式。

分别求出 \({\mathrm{C}}_n^m \bmod{p_i^{a_i}}\) 的答案用 \(CRT\) 合并即可。

因为 \(p_i^{a_i}\) 不一定是质数,所以不能直接去算 \(m!\)\((n-m)!\) 的逆元,因为这个数有可能和 \(p_i^{a_i}\) 不互质,也就不存在逆元。

所以我们需要求出 \(m!\)\(p_i\) 的指数以及把所有的 \(p_i\) 都除掉后的结果,这个可以递归去做。

对于没有 \(p_i\) 的那一部分,我们可以去计算逆元,对于含有 \(p_i\) 的那一部分,我们直接统计分子比分母多含有多少 \(p_i\),用快速幂去计算即可。

时间复杂度 \(plogp\)

代码

P2183 [国家集训队]礼物

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#define rg register
template<typename T>void read(rg T& x){
	x=0;rg int fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	x*=fh;
}
const int maxn=1e5+5;
inline int addmod(rg int now1,rg int now2,rg int mod){
	return now1+=now2,now1>=mod?now1-mod:now1;
}
inline int delmod(rg int now1,rg int now2,rg int mod){
	return now1-=now2,now1<0?now1+mod:now1;
}
inline int mulmod(rg long long now1,rg int now2,rg int mod){
	return now1*=now2,now1>=mod?now1%mod:now1;
}
int ksm(rg int ds,rg int zs,rg int mod){
	rg int nans=1;
	while(zs){
		if(zs&1) nans=mulmod(nans,ds,mod);
		ds=mulmod(ds,ds,mod);
		zs>>=1;
	}
	return nans;
}
int getg(rg int nn,rg int p){
	if(nn<p) return 0;
	return getg(nn/p,p)+nn/p;
}
int getf(rg int nn,rg int p,rg int pk){
	if(nn==0) return 1;
	rg int nans=1,mans=1;
	for(rg int i=1;i<=pk;i++){
		if(i%p) mans=mulmod(mans,i,pk);
	}
	nans=ksm(mans,nn/pk,pk);
	for(rg int i=nn/pk*pk+1;i<=nn;i++){
		if(i%p) nans=mulmod(nans,i,pk);
	}
	return mulmod(nans,getf(nn/p,p,pk),pk);
}
int exgcd(rg int aa,rg int bb,rg int& xx,rg int& yy){
	if(bb==0){
		xx=1,yy=0;
		return aa;
	}
	rg int nans=exgcd(bb,aa%bb,xx,yy);
	rg int t=xx;
	xx=yy;
	yy=t-aa/bb*yy;
	return nans;
}
int getinv(rg int val,rg int mod){
	rg int xx=0,yy=0;
	exgcd(val,mod,xx,yy);
	xx=(xx%mod+mod)%mod;
	return xx;
}
int getC(rg int nn,rg int mm,rg int p,rg int pk){
	rg int cnt=getg(nn,p)-getg(nn-mm,p)-getg(mm,p);
	rg int tmp1=getf(nn-mm,p,pk),tmp2=getf(mm,p,pk),tmp3=getf(nn,p,pk);
	rg int nans=ksm(p,cnt,pk);
	nans=mulmod(nans,tmp3,pk);
	nans=mulmod(nans,getinv(tmp1,pk),pk);
	nans=mulmod(nans,getinv(tmp2,pk),pk);
	return nans;
}
int pri[maxn],pricnt,mi[maxn];
void divid(rg int mod){
	for(rg int i=2;i*i<=mod;i++){
		if(mod%i==0){
			pri[++pricnt]=i;
			mi[pricnt]=1;
			while(mod%i==0){
				mod/=i;
				mi[pricnt]*=i;
			}
		}
	}
	if(mod>1){
		pri[++pricnt]=mod;
		mi[pricnt]=mod;
	}
}
int a[maxn],M[maxn],t[maxn];
int crt(rg int nn,rg int mm,rg int mod){
	for(rg int i=1;i<=pricnt;i++){
		a[i]=getC(nn,mm,pri[i],mi[i]);
		M[i]=mod/mi[i];
		t[i]=getinv(M[i],mi[i]);
	}
	rg int ans=0;
	for(rg int i=1;i<=pricnt;i++){
		ans=addmod(ans,mulmod(a[i],mulmod(M[i],t[i],mod),mod),mod);
	}
	return ans;
}
int w[maxn],sum,ans=1;
int main(){
	int n,m,mod;
	read(mod),read(n),read(m);
	divid(mod);
	for(rg int i=1;i<=m;i++){
		read(w[i]);
	}
	for(rg int i=1;i<=m;i++){
		if(n<w[i]){
			printf("Impossible\n");
			return 0;
		}
		ans=mulmod(ans,crt(n,w[i],mod),mod);
		n-=w[i];
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2021-04-07 19:46  liuchanglc  阅读(85)  评论(0编辑  收藏  举报