bzoj4591 [Shoi2015]超能粒子炮·改——组合数学(+求阶乘逆元新姿势)
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4591
这题不是很裸啊(所以我就不会了)
得稍微推导一下,看这个博客好了:https://blog.csdn.net/All_ice/article/details/68947444
以前求 1~n 的阶乘逆元一直是先用费马小定理求出 n! 的逆元,再每次 *i 递推回去;
这次用了另一种递推求阶乘逆元的方法,其实就是递推求逆元再阶乘起来:https://blog.csdn.net/w4149/article/details/72847276?locationNum=6&fps=1
预处理 C 会稍微快一点(但还是很慢啊)。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; typedef long long ll; int const mod=2333; ll T,n,k,fac[mod+5],inv[mod+5],sum[mod+5][mod+5],c[mod+5][mod+5]; ll C(int n,int m) { if(n<m)return 0; return c[n][m]; // return ((fac[n]*inv[m])%mod*inv[n-m])%mod; } ll Lucas(ll n,ll m) { if(m==0)return 1; return (C(n%mod,m%mod)*Lucas(n/mod,m/mod))%mod; } void init() { fac[0]=1; c[0][0]=1; for(int i=1;i<mod;i++)fac[i]=(fac[i-1]*i)%mod; inv[0]=1; inv[1]=1; for(int i=2;i<mod;i++)inv[i]=((mod-mod/i)*inv[mod%i])%mod;//从2开始! for(int i=1;i<mod;i++)inv[i]=(inv[i]*inv[i-1])%mod; for(int i=0;i<mod;i++) { sum[i][0]=1; c[i][0]=1; if(i) { for(int j=1;j<mod;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod; } for(int j=1;j<mod;j++) sum[i][j]=(sum[i][j-1]+C(i,j))%mod; } } ll ans(ll n,ll k) { if(k<0)return 0; return ((ans(n/mod,k/mod-1)*sum[n%mod][mod-1])%mod +(Lucas(n/mod,k/mod)*sum[n%mod][k%mod])%mod)%mod; } int main() { init(); scanf("%lld",&T); while(T--) { scanf("%lld%lld",&n,&k); printf("%lld\n",ans(n,k)); } return 0; }