exlucas易错点
复习了一下 exlucas的模板,结果写挂四次(都没脸说自己以前写过
是该好好反思一下呢~
错的原因如下:
第一次WA:求阶乘的时候忘了递归处理(n/p)!
第二次WA:求阶乘时把p当成循环节了,循环节应该是(p^k)
第三次WA:把循环节改成(p^k)后,干脆把递归处理(n/p)!改成了递归处理(n/(p^k))! (智障
第四次WA:求(p^k)的逆元直接用(p^k)^(mod-2),然而(p^k)不一定是质数,不能用费马小定理,应该用exgcd求逆元
就因为这几个错误花了我两个小时的时间,其实主要原因是自己没想清楚,连exlucas式子都推错,看题解也太心急了没认真看,导致自己反复错。
还有就是自己数论的基础太差,比如第四次错误就想当然以为(p^k)的逆元是它的(mod-2)次方,连费马小定理的条件都忘了
(PS:好像luogu上此题开O2会全T?相同的代码不开O2都过了)
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 107 4 #define ll long long 5 ll p2(ll x,ll mod) 6 { 7 return x*x%mod; 8 } 9 ll pw(ll x,ll p,ll mod) 10 { 11 return p?p2(pw(x,p/2,mod),mod)*(p&1?x:1)%mod:1; 12 } 13 ll fun(ll n,ll p) 14 { 15 if(n<p)return 0; 16 return fun(n/p,p)+n/p; 17 } 18 ll exfac(ll n,ll p,ll mod) 19 { 20 if(n==0)return 1; 21 ll i,mul=1; 22 for(i=1;i<=mod;i++) 23 if(i%p!=0)mul=mul*i%mod; 24 ll ans=pw(mul,n/mod,mod); 25 ll rest=n%mod; 26 for(i=1;i<=rest;i++) 27 if(i%p!=0)ans=ans*i%mod; 28 return ans*exfac(n/p,p,mod)%mod; 29 } 30 ll exgcd(ll x,ll y,ll &a,ll &b) 31 { 32 ll z; 33 return y?(z=exgcd(y,x%y,b,a),b-=a*(x/y),z):(a=1,b=0,x); 34 } 35 ll getinv(ll a,ll p) 36 { 37 ll x,y; 38 exgcd(a,p,x,y); 39 x=(x%p+p)%p; 40 } 41 ll exlucas(ll n,ll m,ll p,ll mod) 42 { 43 ll ans=exfac(n,p,mod); 44 ans=ans*getinv(exfac(m,p,mod),mod)%mod; 45 ans=ans*getinv(exfac(n-m,p,mod),mod)%mod; 46 ll times=fun(n,p)-fun(m,p)-fun(n-m,p); 47 ans=ans*pw(p,times,mod)%mod; 48 return ans; 49 } 50 ll a[N],b[N],cnt; 51 void getans(ll n,ll m,ll p) 52 { 53 ll t=sqrt(p),i,x=p; 54 for(i=2;i<=p;i++) 55 { 56 if(x%i==0) 57 { 58 ll mod=1; 59 while(x%i==0)x/=i,mod*=i; 60 ll res=exlucas(n,m,i,mod); 61 //printf("%lld\n",res); 62 a[++cnt]=res,b[cnt]=mod; 63 } 64 } 65 } 66 int main() 67 { 68 ll n,m,p,i; 69 scanf("%lld%lld%lld",&n,&m,&p); 70 getans(n,m,p); 71 ll ans=0; 72 for(i=1;i<=cnt;i++) 73 { 74 ll M=p/b[i]; 75 ll x=a[i]*M%p*getinv(M,b[i])%p; 76 ans=(ans+x)%p; 77 } 78 printf("%lld\n",ans); 79 return 0; 80 }