1. 程式人生 > 實用技巧 >P4345 [SHOI2015]超能粒子炮·改 題解

P4345 [SHOI2015]超能粒子炮·改 題解

此題對於50%的資料可以直接用盧卡斯定理卡個\(O(T\sqrt n)\)的做法卡過去,但你看後面的資料你就會發現事情絕對沒有那麼簡單,應該有億點技巧在裡面,這時候我們就要開始用我們的lucas定理推柿子了

前置芝士lucas定理

\(C_{n}^{m}\%p=C_{n/p}^{m/p}*C_{n\%p}^{m\%p}\%p\)
證明建議自己去百度,讀者自證不難

正文

有了這個之後我們就可以開始推柿子了
簡化題面我們可以得到以下柿子\(\sum_{i=0}^kC_{n}^i\%p\)

這裡我們設\(f(n,k)=\sum_{i=0}^kC_{n}^i\)\(p=2333\)

\(lucas\)定理將後面拆開之後可以得到\(f(n,k)=\sum_{i=0}^kC_{n}^i=\sum_{i=0}^kC_{n/p}^{i/p}*C_{n\%p}^{i\%p}\)

隨後考慮將兩個\(C\)拆開,此時考慮\(i\%p\)的貢獻,可以發現p是一個質數,考慮i在小於p的區間內所得到的\(i\%p\)總共有\(0\)~\(p-1\)的貢獻,對於i大於p的情況也是同理,那麼我們可以得到
\(C_{n/p}^0\sum_{i=0}^{p-1}C_{n\%p}^i+C_{n/p}^1\sum_{i=0}^{p-1}C_{n\%p}^i+...+C_{n/p}^{k/p-1}\sum_{i=0}^{p-1}C_{n\%p}^i+C_{n/p}^{k/p}\sum_{i=0}^{k\%p}C_{n\%p}^i\)

對於最後一塊我們先不做處理,然後我們發現可以提公因式,因此我們將\(\sum_{i=0}^{P-1}\times C_{n\%P}^{i}\)

提出來可以得到\((C_{n/p}^0+C_{n/p}^1+...C_{n/p}^{k/p-1})*\sum_{i=0}^{p-1}C_{n\%p}^i\)

對於前面你還可以提出來一個\(\sum\)變成\(\sum_{i=0}^{k/p-1}C_{n\%p}^i\)

然後我們套用\(f(n,k)=\sum_{i=0}^kC_{n}^i\),可以變成\(f(n\%p,p-1)*f(\lfloor \frac np\rfloor , \lfloor \frac kp \rfloor-1)\)

而對於我們剩下的\(C_{n/p}^{k/p}\sum_{i=0}^{k\%p}C_{n\%p}^i\)

我們也可以拆開變成\(C_{n/p}^{k/p}*f(n\%p,m\%p)\)

那麼最後答案可以變為\(f(n\%p,p-1)*f(\lfloor \frac np\rfloor , \lfloor \frac kp \rfloor-1)+C_{n/p}^{k/p}*f(n\%p,m\%p)\)

對於f和c的預處理我們只需要先處理p以內的即可

#include<bits/stdc++.h>

#define LL long long

using namespace std;

template <typename T> void read(T & t) {              
    t = 0;int f = 1;char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-')f =- 1;ch = getchar();}
    do{t = t * 10 + ch - '0';ch = getchar();}while(ch >= '0' && ch <= '9');t *= f;
}

const int kato = 3e3 + 1;

#define mod 2333

int t;

LL inv[kato] , sum[kato] , f[kato][kato];

inline LL quick_pow(LL a , LL b){
    LL res = 1;
    for(; b ; b >>= 1 , a = a * a % mod){
        if(b & 1){
            res = res * a % mod;
        }
    }
    return res % mod;
}

inline LL c(LL n , LL m){
    if(n < m){
        return 0;   
    }
    if(n < mod && m < mod){
        return (sum[n] * quick_pow(sum[m] * sum[n - m] % mod , mod - 2) ) % mod;
    }
    return c(n / mod , m / mod) * c(n % mod , m % mod) % mod;
}

inline LL lucas(LL n , LL m){
    if(n < mod && m < mod){
        return f[n][m];
    }
    else{
        return (c(n / mod , m / mod) * lucas(n % mod , m % mod) + lucas(n % mod , mod - 1) * lucas(n / mod , m / mod - 1)) % mod;
    }
}

inline void init(){
    sum[0] = 1;
    for(int i = 1;i <= kato;i ++){
        sum[i] = (sum[i - 1] * (i % mod) )% mod;
    }
    for(int i = 0;i <= mod;i ++){
        f[i][0] = c(i , 0);
        for(int j = 1;j <= mod;j ++){
            f[i][j] = (f[i][j - 1] + c(i , j)) % mod;
        }
    }
}



inline int Ame_(){
    // freopen("b.in" , "r" , stdin);
    // freopen("b.out" , "w" , stdout);
    read(t);
    init();
    for(LL n , k; t --> 0 ;){
        read(n);read(k);
        LL ans = 0;
        ans = lucas(n , k);
        printf("%lld\n" , ans);
    }
    // fclose(stdin);
    // fclose(stdout);
    return 0;
}

int Ame__ = Ame_();

int main(){;}