bzoj4591 [Shoi2015]超能粒子炮·改
Description
曾经发明了脑洞治疗仪&超能粒子炮的发明家SHTSC又公开了他的新发明:超能粒子炮·改--一种可以发射威力更加强大的粒子流的神秘装置。超能粒子炮·改相比超能粒子炮,在威力上有了本质的提升。它有三个参数n,k。它会向编号为0到k的位置发射威力为C(n,k) mod 2333的粒子流。现在SHTSC给出了他的超能粒子炮·改的参数,让你求其发射的粒子流的威力之和模2333。
Input
第一行一个整数t。表示数据组数。
之后t行,每行二个整数n,k。含义如题面描述。
k<=n<=10^18,t<=10^5
Output
t行每行一个整数,表示其粒子流的威力之和模2333的值。
Sample Input
1
5 5
5 5
Sample Output
32
正解:组合数学+$lucas$定理。
$Ans=\sum_{k=0}^{m}\binom{n}{k} \ mod \ p$
令$\binom{n}{k} \ mod \ p=lucas(n,k,p)$,那么由$lucas$定理,$lucas(n,k,p)=\binom{n \ mod \ p}{k \ mod \ p}*lucas(n/p,k/p,p)$
我们把上式带入$Ans$中,令$m=p*q+r$,可以发现$Ans=\sum_{k=0}^{n \ mod \ p}\binom{n \ mod \ p}{k}*\sum_{k=0}^{q-1}\binom{n/p}{k} \ mod \ p+\sum_{k=0}^{r}\binom{n \mod \ p}{k}*lucas(n/p,q,p)$
预处理出$p$以内的组合数和杨辉三角每一层的前缀和,直接写一个递归函数调用即可。
因为递归函数和$lucas$函数的层数都不超过$log_{p}n$层,所以单次询问复杂度很低,只有大约$40$次运算。
1 //It is made by wfj_2048~ 2 #include <algorithm> 3 #include <iostream> 4 #include <cstring> 5 #include <cstdlib> 6 #include <cstdio> 7 #include <vector> 8 #include <cmath> 9 #include <queue> 10 #include <stack> 11 #include <map> 12 #include <set> 13 #define inf (1<<30) 14 #define rhl (2333) 15 #define il inline 16 #define RG register 17 #define ll long long 18 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) 19 20 using namespace std; 21 22 ll sum[3010][3010],c[3010][3010],n,m; 23 24 il ll gi(){ 25 RG ll x=0,q=1; RG char ch=getchar(); 26 while ((ch<'0' || ch>'9') && ch!='-') ch=getchar(); 27 if (ch=='-') q=-1,ch=getchar(); 28 while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); 29 return q*x; 30 } 31 32 il void pre(){ 33 sum[0][0]=c[0][0]=1; 34 for (RG ll i=1;i<rhl;++i){ 35 sum[i][0]=c[i][0]=c[i][i]=1; 36 for (RG ll j=1;j<i;++j){ 37 c[i][j]=c[i-1][j-1]+c[i-1][j]; 38 if (c[i][j]>=rhl) c[i][j]-=rhl; 39 sum[i][j]=sum[i][j-1]+c[i][j]; 40 if (sum[i][j]>=rhl) sum[i][j]-=rhl; 41 } 42 sum[i][i]=sum[i][i-1]+1; 43 } 44 return; 45 } 46 47 il ll lucas(RG ll n,RG ll m){ 48 if (!m) return 1; RG ll res=c[n%rhl][m%rhl]; 49 if (!res) return 0; return res*lucas(n/rhl,m/rhl)%rhl; 50 } 51 52 il ll calc(RG ll n,RG ll m){ 53 if (m<0) return 0; if (!m) return 1; 54 RG ll k=m/rhl,ans=sum[n%rhl][min(m-k*rhl,n%rhl)]*lucas(n/rhl,k)%rhl; 55 return (ans+sum[n%rhl][n%rhl]*calc(n/rhl,k-1))%rhl; 56 } 57 58 int main(){ 59 File("lucas"); 60 RG ll T=gi(); pre(); 61 while (T--){ 62 n=gi(),m=gi(); 63 printf("%lld\n",calc(n,m)); 64 } 65 return 0; 66 }