[SHOI2015]超能粒子炮·改
[SHOI2015]超能粒子炮·改
[题目链接]
[思路要点]
看到组合数模数是 \(2333\) 这样一个小质数,很容易想到 \(\mathrm{Lucas}\) 定理
但是如果直接按 \(2333\) 进制分解,发现没法做了,于是我们使用 $C_a^b % p = C_{\frac a p}^{\frac b p} \cdot C_{a \m%}^{b \mod%\mod p%
令 \(f(n,k) = \sum_{i=0}^{k} C_n^i\),那么要求的就是 \(f(n,k)\)
\[\begin{align}
f(n, k) &= \sum_{i=0}^{k} C_{n}^i\\
&= \sum_{i=0}^{k} C_{\frac n p}^{\frac i p} \cdot C_{n \% p}^{i \% p} \\
&= \sum_{i=0}^{\frac k p - 1}C_{\frac n p}^ i \cdot \left ( \sum _{j = 0}^{p-1}C_{n \% p}^{j}\right)+C_{\frac n p}^{\frac k p}\sum_{j=0}^{k\% p}C_{n \% p}^{j}\\
&=f(\frac n p, \frac k p - 1) \cdot f(n \% p, p - 1)+ C_{\frac n p}^{\frac kp}f(n \% p,k \% p)
\end{align}
\]
由于 \(p\) 是 \(2333\),我们可以把 \(f(n\% p,p-1)\) 和 \(f(n\% p, k\% p)\) 都预处理出来,然后递归求解,\(C_{\frac n p}^{\frac k p}\) 使用 \(\mathrm{Lucas}\) 求解。
每次 \(n\) 会除以一个 \(p\),所以复杂度 \(\mathcal{O}(p^2+T\cdot \log_p^2n)\)
[代码]
#include<iostream>
#include<cstring>
#include<cstdio>
#define re register
#define LL long long
#define maxn 2335
const int P=2333;
LL c[maxn+2][maxn+2];
LL f[maxn+2][maxn+2];
inline LL Lucas(LL n,LL m)
{
if(!m) return 1;
if(n==m) return 1;
if(n<m) return 0;
return c[n%P][m%P]*Lucas(n/P,m/P)%P;
}
inline LL F(LL n,LL k)
{
if(k<0) return 0;
if(!n) return 1;
if(!k) return 1;
if(n<P&&k<P) return f[n][k];
return (F(n/P,k/P-1)*f[n%P][P-1]%P+Lucas(n/P,k/P)*f[n%P][k%P]%P)%P;
}
int main()
{
int T;
scanf("%d",&T);
c[0][0]=1;
for(re int i=1;i<=maxn;i++)
c[i][i]=c[i][0]=1;
for(re int i=1;i<=maxn;i++)
for(re int j=1;j<i;j++)
c[i][j]=(c[i-1][j]+c[i-1][j-1])%P;
f[0][0]=1;
for(re int i=1;i<=maxn;i++)
f[i][0]=1;
for(re int i=0;i<=maxn;i++)
for(re int j=1;j<=maxn;j++)
f[i][j]=(c[i][j]+f[i][j-1])%P;
LL n,k;
while(T--)
{
scanf("%lld%lld",&n,&k);
printf("%lld\n",F(n,k));
}
return 0;
}