1.n,m<=1000 p随意
暴力
2.n,m<=10^6 p<=10^9 素数合数无影响
分解质因数,然后快速幂
如果p是素数,且多次询问可以预处理阶乘的模以及阶乘的逆元
3.n,m<=10^9 p<=10^5且是质数
lucas定理 多次询问也可以预处理
4.n,m<=10^9 p<=10^5且是合数
目前还不会做系列。。。
我们把p拆成 pi^ci 然后对每个pi求模,最后CRT(中国剩余定理)合并一下。
考虑如何求这个。
其实我们只要考虑n!mod p^c即可
然后我们发现:
n! = [ 1 * 2 * 4 * 5 * 7 * 8 * … * 16 * 17 * 19 ] * (3 * 6 * 9 * 12 * 15 * 18)= [ 1 * 2 * 4 * 5 * 7 * 8 * … * 16 * 17* 19 ] * 3^6( 1 * 2 * 3 * 4 * 5 * 6)
把p单独处理之后发现前面是以p^c为周期的(显然),然后后面划归成了n/p,可以递归求解。然后p的次数就是n/p。
这样我们就可以预处理阶乘,将是p的倍数的位数省略,以1代过。最后p快速幂一下就好。在分子上的话其实一样,只需要预处理成阶乘的逆元即可。
说起来很麻烦,还是看代码比较好。。。
void divide(int n) { for(int i=2;i*i<=n;i++)if(n%i==0) { p[++cnt]=i;pc[cnt]=1; while(n%i==0)pc[cnt]*=i,n/=i; } if(n>1)p[++cnt]=n,pc[cnt]=n; } inline ll power(ll x,ll y,ll p) { ll t=1; for(;y;y>>=1,x=x*x%p) if(y&1)t=t*x%p; return t; } inline void exgcd(ll a,ll b,ll &x,ll &y) { if(!b){x=1;y=0;return;} exgcd(b,a%b,x,y); ll t=x;x=y,y=t-a/b*y; } inline ll inv(ll a,ll b) { ll x,y; exgcd(a,b,x,y); return (x%b+b)%b; } inline ll calc(ll n,ll p,ll pc) { if(n<p)return fac[n]; now+=n/p; return fac[n%pc]*power(fac[pc-1],n/pc,pc)%pc*calc(n/p,p,pc)%pc; } inline ll solve(ll p,ll pc) { fac[0]=1; for1(i,pc-1)fac[i]=fac[i-1]*(i%p?i:1)%pc; now=0; ll t1=calc(n,p,pc),t2=1,cx=now,cy; for1(i,pc-1)fac[i]=fac[i-1]*(i%p?inv(i,pc):1)%pc; now=0; for1(i,m)t2=t2*calc(v[i],p,pc)%pc; cy=now; return t1*t2%pc*power(p,cx-cy,pc)%pc; } int main() { freopen("input.txt","r",stdin); freopen("output.txt","w",stdout); mod=read();n=read();m=read(); for1(i,m)v[i]=read(),sum+=v[i]; if(sum>n){printf("Impossible ");return 0;} if(sum<n)v[++m]=n-sum; divide(mod); ll ans=0; for1(i,cnt) { ll t1=solve(p[i],pc[i]),t2=inv(mod/pc[i],pc[i]); (ans+=t1*t2%mod*(mod/pc[i])%mod)%=mod; } cout<<(ans+mod)%mod<<endl; return 0; }
5.n,m<=10^9 p<=10^9
目前无解决方法貌似?当然你要是和我说 pi^ci<=10^5 我就呵呵了。。。