[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;
}
posted @ 2022-07-05 19:34  lxzy  阅读(39)  评论(0)    收藏  举报