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;
}
View Code

 

posted @ 2018-09-21 12:51  CzxingcHen  阅读(168)  评论(0编辑  收藏  举报