P4345 [SHOI2015]超能粒子炮·改
看到数据和模数大小就知道要上 lucas 了
然后开始愉快地推公式:
答案为 $\sum _{i=0}^kC_{n}^{i}\ (mod\ 2333)$
设 $f [ i ] [ j ] = \sum _{k=0}^jC_{i}^{k}\ (mod\ 2333)\ ,\ P=2333$
那么根据 lucas 定理得 $f[n][k]=\sum _{i=0}^k {C_{n\%P}^{i\%P}C_{n/P}^{i/p}}$
看到 $i/P$ 容易想到整除分块,那就把 $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}\sum _{i=0}^{k\%P}{C_{n\%P}^{i}}$
把$\sum _{i=0}^{p-1}{C_{n\%P}^{i}}$ 提出来,得到
$=\sum _{i=0}^{p-1}{C_{n\%P}^{i}}(C_{n/P}^{0}+C_{n/P}^{1}+...+C_{n/P}^{k/P-1})+ C_{n/P}^{k/P}\sum _{i=0}^{k\%P}{C_{n\%P}^{i}}$
那就可以写成 $=f[n\%P][P-1]\cdot f[n/P][k/P-1]+ C_{n/P}^{k/P}f[n\%P][k\%P]$
然后就可以递归下去求了
注意long long
#include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #include<cstring> using namespace std; typedef long long ll; inline ll read() { ll x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int M=3007,mo=2333; inline ll fk(ll x) { return x>=mo ? x-mo : x; } int T; ll N,K,f[M][M],C[M][M]; inline ll lucas(ll a,ll b)//lucas不解释 { if(a<b) return 0; if(!b||a==b) return 1; return C[a%mo][b%mo]*lucas(a/mo,b/mo)%mo; } inline ll F(ll n,ll k) { if(k<0) return 0; if(!n||!k) return 1;//边界 if(n<mo&&k<mo) return f[n][k];//边界 return fk( F(n/mo,k/mo-1)*f[n%mo][mo-1]%mo + lucas(n/mo,k/mo)*f[n%mo][k%mo]%mo ); } void pre()//预处理,注意C和f范围不同 { C[0][0]=f[0][0]=1; for(int i=1;i<=mo;i++) { C[i][0]=C[i][i]=f[i][0]=1; for(int j=1;j<i;j++) C[i][j]=fk(C[i-1][j]+C[i-1][j-1]); } for(int i=0;i<=mo;i++) for(int j=1;j<=mo;j++) f[i][j]=fk(f[i][j-1]+C[i][j]); } int main() { pre(); scanf("%d",&T); while(T--) { N=read(); K=read(); printf("%lld\n",F(N,K)); } return 0; }