[SHOI2015]超能粒子炮·改
做题时间:2022.7.5
\(【题目描述】\)
给定 \(t(t\leq 10^5)\) 组正整数 \(n,k(n,k\leq 10^{18})\) ,求:
\[\sum\limits_{i=0}^k C(n,i)\mod 2333
\]
\(【输入格式】\)
第一行一个整数 \(t\)
第 \(2\) 至 \(t+1\) 行,每行两个整数表示 \(n,k\)
\(【输出格式】\)
共 \(t\) 行表示每组 \(n,k\) 对应的答案
\(【考点】\)
卢卡斯定理,递归,分块
\(【做法】\)
令 \(p=2333\)
\(n\) 与 \(k\) 很大,而模数很小,先考虑卢卡斯定理,得:
\[\sum\limits_{i=0}^{k}C(\frac{n}{p},\frac{i}{p})\cdot C(n\%p,i\%p)\mod p
\]
第一项中 \(i/p\) 的值是每 \(p-1\) 个均相同,而第二项中 \(i\%p\) 的值是没 \(p-1\) 个循环,可以考虑分块,将 \(k+1\) 项划分成大小为 \(p\) 的块,得:
\[C(\frac{n}{p},0)\cdot \sum\limits_{i=0}^{p-1}C({n\%p},i)+C(\frac{n}{p},1)\cdot \sum\limits_{i=0}^{p-1}C(n\%p,i)+...+C(\frac{n}{p},\frac{k}{p}-1)\cdot \sum\limits_{i=0}^{p-1}C(n\%p,i)+C(\frac{n}{p},\frac{k}{p}-1)\cdot \sum\limits_{i=0}^{k\% p}C(n\%p,i)
\]
将前 \(k/p\) 项的公因式 \(\sum\limits_{i=0}^{p-1}C({n\%p},i)\) 提出,得:
\[\sum\limits_{i=0}^{p-1}C(n\%p,i)\cdot \sum\limits_{j=0}^{k/p}C(\frac{n}{p},j)+C(\frac{n}{p},\frac{k}{p}-1)\cdot \sum\limits_{i=0}^{k\% p}C(n\%p,i)
\]
由于 \(p\) 很小,因此第一个和第三个求和部分可以预处理直接求出,而第二项的组合数部分直接用卢卡斯定理求解;第二个求和式子形式与原式相同,且 \(n,k\) 都极大地减小了,因此可以考虑递归求解第二项。
设 \(f(n,k)=\sum\limits_{i=0}^k C(n,i)\) ,则上述式子可以化为:
\[f(n,k)\equiv f(n\%p,p-1)\cdot f(\frac{n}{p},\frac{k}{p})+C(\frac{n}{p},\frac{k}{p})\cdot f(n\%p,k\%p) \pmod p
\]
直接求解即可。
(顺带把预处理部分直接化为函数了
\(【代码】\)
#include<cstdio>
#include<iomanip>
using namespace std;
const int N=2e3+5e2;
typedef long long ll;
ll c[N][N],p=2333;
ll f[N][N];
inline void Init()
{
c[0][0]=1;
for(int i=1;i<=2334;i++) c[i][i]=c[i][0]=1;
for(int i=1;i<=2334;i++){
for(int j=1;j<i;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%p;
}
//预处理小的F
f[0][0]=1;
for(int i=1;i<=2334;i++) f[i][0]=1;
for(int i=0;i<=2334;i++){
for(int j=1;j<=2334;j++){
f[i][j]=(c[i][j]+f[i][j-1])%p;
}
}
}
inline ll Lucas(ll n,ll m,ll p)
{
if(n<m) return 0;
if(m==0) return 1;
if(n==m) return 1;
return Lucas(n/p,m/p,p)%p*c[n%p][m%p]%p;
}
inline ll Solve(ll n,ll k)//f函数的递归
{
if(k<0) return 0;
if(n==0||k==0) return 1;
if(n<=p&&k<=p) return f[n][k];
return (Solve(n%p,p-1)%p*Solve(n/p,k/p-1)%p+Solve(n%p,k%p)%p*Lucas(n/p,k/p,p)%p)%p;
}
int main()
{
Init();
int t;ll n,k;
scanf("%d",&t);
while(t--){
scanf("%lld%lld",&n,&k);
printf("%lld\n",Solve(n,k));
}
return 0;
}

浙公网安备 33010602011771号