P4345[SHOI2015]超能粒子炮·改【Lucas定理,类欧】

正题

题目链接:https://www.luogu.com.cn/problem/P4345


题目大意

\(T\)组询问,给出\(n,k\)

\[\sum_{i=0}^{k}\binom{n}{i} \]

\(2333\)取模的值

\(1\leq T\leq 10^5,1\leq k\leq n\leq 10^{18}\)


解题思路

因为模数很小,可以考虑用\(Lucas\)定理,然后考虑怎么优化复杂度。
对于给出的\(n,k\)分成两个部分,第一部分是由\(k\)前面若干段长度为\(P\)的整段构成,这一部分的答案我们发现对于\(C_{\lfloor\frac{n}{P}\rfloor}^{\lfloor\frac{m}{P}\rfloor}\times C^{n\%p}_{m\% p}\)这两个值,后面那一个值的和是确定的,是\(\sum_{i=1}^kC_{n\%p}^k\),前面那一部分的值我们可以递归下去计算。

然后第二部分是剩下的散段,这个部分我们也是自直接递归下去算就可以了

时间复杂度\(O(T\log n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll P=2333;
ll n,k,t,S[P][P],C[P][P];
ll Lucas(ll n,ll k){
	if(!k)return 1ll;
	if(!C[n%P][k%P])return 0;
	return Lucas(n/P,k/P)*C[n%P][k%P]%P;
}
ll solve(ll n,ll k){
	if(k<0)return 0;
	if(n<P)return S[n][min(n,k)];
	ll tmp=solve(n/P,k/P-1)*S[n%P][n%P]%P;
	tmp=(tmp+solve(n%P,k%P)*Lucas(n/P,k/P)%P)%P;
	return tmp;
}
signed main()
{
	C[0][0]=S[0][0]=1;
	for(ll i=1;i<P;i++)
		for(ll j=0;j<=i;j++)
			C[i][j]=((j?C[i-1][j-1]:0)+C[i-1][j])%P;
	for(ll i=1;i<P;i++){
		S[i][0]=C[i][0];
		for(ll j=1;j<=i;j++)
			(S[i][j]=S[i][j-1]+C[i][j])%=P;
	}
	scanf("%lld",&t);
	while(t--){
		scanf("%lld%lld",&n,&k);
		printf("%lld\n",solve(n,k));
	}
	return 0;
}
原文地址:https://www.cnblogs.com/QuantAsk/p/14423650.html