Luogu 4345 [SHOI2015]超能粒子炮·改
BZOJ4591
并不会写的组合数学。
我们设$f(n, k) = \sum_{i= 0}^{k}\binom{n}{i}$,那么每一个询问要求的就是$f(n, k)$。
发现$f(i, j)$其实可以递推:
$f(i, 0) = 1$
$f(i, j) = f(i, j - 1) + \binom{i}{j}$
看上去没什么用处,但是我们还有$Lucas$定理。
$f(n, k) = \sum_{i = 0}^{k}\binom{n}{i} \ (Mod\ P) \ =\sum_{i = 0}^{k}\binom{\left \lfloor \frac{n}{P} \right \rfloor}{\left \lfloor \frac{i}{P} \right \rfloor} * \binom{n\%p}{i\%p}$
看到了$\left \lfloor \frac{i}{P} \right \rfloor$这个东西,其实我们知道这个东西一共只有$\sqrt{P}$种取值,所以我们把一样的东西提出来,一共有$\left \lfloor \frac{k}{p} \right \rfloor - 1$个完整的循环节,还有一个不完整的循环节是$\binom{\left \lfloor \frac{n}{P} \right \rfloor}{\left \lfloor \frac{k}{P} \right \rfloor}\sum_{i = 0}^{k \% P}\binom{n\% P}{i}$。
加起来:
$\sum_{i = 0}^{\left \lfloor \frac{k}{p} \right \rfloor - 1}\binom{\left \lfloor \frac{n}{P} \right \rfloor}{i}\sum_{j = 0}^{P - 1}\binom{n \% P}{j} + \binom{\left \lfloor \frac{n}{P} \right \rfloor}{\left \lfloor \frac{k}{P} \right \rfloor}\sum_{i = 0}^{k \% P}\binom{n\% P}{i}$
发现其实是若干个子问题的叠加:
$f(\left \lfloor \frac{n}{P} \right \rfloor , \left \lfloor \frac{k}{P} \right \rfloor - 1)* f(n \% P , P - 1) + f(n \% P , k \% P) * \binom{\left \lfloor \frac{n}{P} \right \rfloor}{\left \lfloor \frac{k}{P} \right \rfloor}$
这样子对于所有的$f$,我们直接递归求解,小于模数的$f$可以直接递推预处理,可以发现递归深度一定不会超过$log$层,还有一个组合数,用$Lucas$定理算一波就好了。
时间复杂度$O(P^2 + Tlogn)$,$log$的底数为$2333$。
Code:
#include <cstdio> #include <cstring> using namespace std; typedef long long ll; const int N = 2505; const int P = 2333; int testCase, c[N][N], f[N][N]; inline int lucas(ll n, ll m) { if(m == 0LL) return 1; if(n < P && m < P) return c[n][m]; return lucas(n / P, m / P) * c[n % P][m % P] % P; } inline int solve(ll n, ll k) { if(k == -1LL) return 0; if(n < P && k < P) return f[n][k]; return (solve(n / P, k / P - 1) * solve(n % P, P - 1) % P + lucas(n / P, k / P) * solve(n % P, k % P) % P) % P; } int main() { c[0][0] = 1; for(int i = 1; i <= P; i++) { c[i][0] = 1; for(int j = 1; j <= i; j++) c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % P; } for(int i = 0; i <= P; i++) { f[i][0] = 1; for(int j = 1; j <= P; j++) f[i][j] = (f[i][j - 1] + c[i][j]) % P; } for(scanf("%d", &testCase); testCase--; ) { ll n, K; scanf("%lld%lld", &n, &K); printf("%d\n", solve(n, K)); } return 0; }