P4345 [SHOI2015]超能粒子炮·改 题解
此题对于50%的数据可以直接用\(lucas\)定理卡过去,但你看后面的数据你就会发现事情绝对没有那么简单,应该有亿点技巧在里面,这时候我们就要开始用我们的\(lucas\)定理推柿子了
前置芝士\(lucas\)定理
\(C_{n}^{m}\%p=C_{n/p}^{m/p}*C_{n\%p}^{m\%p}\%p\)
证明建议自己去百度,读者自证不难
正文
有了这个之后我们就可以开始推柿子了
简化题面我们可以得到以下柿子\(\begin{aligned}\sum_{i=0}^kC_{n}^i\%p\end{aligned}\)
这里我们设\(f(n,k)=\sum_{i=0}^kC_{n}^i\),\(p=2333\)
用\(lucas\)定理将后面拆开之后可以得到\(\begin{aligned}f(n,k)=\sum_{i=0}^kC_{n}^i=\sum_{i=0}^kC_{n/p}^{i/p}*C_{n\%p}^{i\%p}\end{aligned}\)
随后考虑将两个\(C\)拆开,此时考虑\(i\%p\)的贡献,可以发现p是一个质数,考虑i在小于p的区间内所得到的\(i\%p\)总共有\(0\)~\(p-1\)的贡献,对于i大于p的情况也是同理,那么我们可以得到
\(\begin{aligned}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\end{aligned}\)
对于最后一块我们先不做处理,然后我们发现可以提公因式,因此我们将\(\begin{aligned}\sum_{i=0}^{P-1}\times C_{n\%P}^{i}\end{aligned}\)
提出来可以得到\(\begin{aligned}(C_{n/p}^0+C_{n/p}^1+...C_{n/p}^{k/p-1})*\sum_{i=0}^{p-1}C_{n\%p}^i\end{aligned}\)
对于前面你还可以提出来一个\(\sum\)变成\(\begin{aligned}\sum_{i=0}^{k/p-1}C_{n\%p}^i\end{aligned}\)
然后我们套用\(\begin{aligned}f(n,k)=\sum_{i=0}^kC_{n}^i\end{aligned}\),可以变成\(\begin{aligned}f(n\%p,p-1)*f(\lfloor \frac np\rfloor , \lfloor \frac kp \rfloor-1)\end{aligned}\)
而对于我们剩下的\(\begin{aligned}C_{n/p}^{k/p}\sum_{i=0}^{k\%p}C_{n\%p}^i\end{aligned}\)
我们也可以拆开变成\(\begin{aligned}C_{n/p}^{k/p}*f(n\%p,m\%p)\end{aligned}\)
那么最后答案可以变为\(\begin{aligned}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)\end{aligned}\)
对于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(){;}