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(){;}