[BZOJ4591]超能粒子炮
預備知識是lucas定理:$\begin{align*}\binom{n}{k}\equiv\binom{\left\lfloor\frac np\right\rfloor}{\left\lfloor\frac kp\right\rfloor}\binom{n\%p}{k\%p}(\bmod p)\end{align*}$,這裏的$p$是質數
證明如下
首先,由費馬小定理得$(1+x)^p\equiv1+x\equiv1+x^p(\bmod p)$
$\begin{align*}(1+x)^n&\equiv(1+x)^{p\left\lfloor\frac np\right\rfloor}(1+x)^{n\%p}\\&\equiv\left(1+x^p\right)^{\left\lfloor\frac np\right\rfloor}(1+x)^{n\%p}\\&\equiv\left(\sum\limits_{i=0}^{\left\lfloor\frac np\right\rfloor}\binom{\left\lfloor\frac np\right\rfloor}ix^{pi}\right)\left(\sum\limits_{i=0}^{n\%p}\binom{n\%p}ix^i\right)\end{align*}$
對兩邊取$x^k$的系數,立得$\begin{align*}\binom{n}{k}\equiv\binom{\left\lfloor\frac np\right\rfloor}{\left\lfloor\frac kp\right\rfloor}\binom{n\%p}{k\%p}(\bmod p)\end{align*}$
再看這題,直接推公式
$\begin{align*}f(n,k)&\equiv\sum\limits_{i=0}^k\binom ni\\&\equiv\sum\limits_{i=0}^{\left\lfloor\frac kp\right\rfloor p-1}\binom{\left\lfloor\frac np\right\rfloor}{\left\lfloor\frac ip\right\rfloor}\binom{n\%p}{i\%p}+\sum\limits_{i=\left\lfloor\frac kp\right\rfloor p}^k\binom{\left\lfloor\frac np\right\rfloor}{\left\lfloor\frac ip\right\rfloor}\binom{n\%p}{i\%p}\\&\equiv\left(\sum\limits_{x=0}^{\left\lfloor\frac kp\right\rfloor-1}\binom{\left\lfloor\frac np\right\rfloor}x\right)\left(\sum\limits_{y=0}^{p-1}\binom{n\%p}y\right)+\binom{\left\lfloor\frac np\right\rfloor}{\left\lfloor\frac kp\right\rfloor}\sum\limits_{i=0}^{k\%p}\binom{n\%p}i\\&\equiv f\left(\left\lfloor\frac np\right\rfloor,\left\lfloor\frac kp\right\rfloor-1\right)f(n\%p,p-1)+\binom{\left\lfloor\frac np\right\rfloor}{\left\lfloor\frac kp\right\rfloor}f(n\%p,k\%p)\end{align*}$
所以預處理上下標在$[0,2332]$內的組合數,每次詢問遞歸計算即可
#include<stdio.h> typedef long long ll; const int p=2333; int C[p][p],S[p][p]; int get(ll n,ll k){ if(n<k)return 0; if(n<p&&k<p)return C[n][k]; return get(n/p,k/p)*get(n%p,k%p)%p; } int f(ll n,ll k){ if(k<0)return 0; return(f(n/p,k/p-1)*S[n%p][p-1]+get(n/p,k/p)*S[n%p][k%p])%p; } int main(){ int i,j,t; ll n,k; C[0][0]=1; for(i=1;i<p;i++){ C[i][0]=1; for(j=1;j<p;j++)C[i][j]=(C[i-1][j]+C[i-1][j-1])%p; } for(i=0;i<p;i++){ S[i][0]=1; for(j=1;j<p;j++)S[i][j]=(S[i][j-1]+C[i][j])%p; } scanf("%d",&t); while(t--){ scanf("%lld%lld",&n,&k); printf("%d\n",f(n,k)); } }
[BZOJ4591]超能粒子炮