题目连接:Harvest of Apples
题意:给出一个n和m,求C(0,n)+C(1,n)+.....+C(m,n)。(样例组数为1e5)
题解:首先先把阶乘和逆元预处理出来,这样就可O(1)将C(m,n)求出来了。但这样还是会超时,所以接下来要分块,每隔500个处理出C(1~m,n)的结果。然后还要知道一个性质 C(a,b) = ∑C(t1,x)*C(t2,y) (x+y<=b),这样我们就可以将给出的m和n分为两个组,其中一个组中元素的个数为500的倍数。然后我们对于另一个组枚举可能的t2然后求和,每次询问最大的操作次数就变成了500次。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 const LL mod = 1e9 + 7; 5 typedef pair<int,int> P; 6 const int MAX_N = 1e5+9; 7 int N,M,T,S; 8 LL Jc[MAX_N]; 9 LL ni_[MAX_N]; 10 LL tran[309][MAX_N]; 11 LL tr[509][509]; 12 //费马小定理求逆元 13 LL pow(LL a, LL n, LL p) //快速幂 a^n % p 14 { 15 LL ans = 1; 16 while(n) 17 { 18 if(n & 1) ans = ans * a % p; 19 a = a * a % p; 20 n >>= 1; 21 } 22 return ans; 23 } 24 25 LL niYuan(LL a, LL b) //费马小定理求逆元 26 { 27 return pow(a, b - 2, b); 28 } 29 30 void calJc() //求maxn以内的数的阶乘 31 { 32 Jc[0] = Jc[1] = 1; 33 for(LL i = 2; i < MAX_N; i++) 34 Jc[i] = Jc[i - 1] * i % mod; 35 } 36 void calni(){ 37 for(int i=0;i<MAX_N;i++){ 38 ni_[i] = niYuan(Jc[i],mod); 39 } 40 } 41 42 LL C(LL a, LL b) //计算C(a, b) 43 { 44 return Jc[a] * ni_[b] % mod 45 * ni_[a-b] % mod; 46 } 47 48 void init(){ 49 for(int i=1;i<MAX_N/500;i++){ 50 for(int j=0;j<MAX_N;j++){ 51 tran[i][j] = C(500*i,j); 52 if(j>0) tran[i][j] = (tran[i][j] + tran[i][j-1])%mod; 53 } 54 } 55 56 for(int i=0;i<509;i++){ 57 for(int j=0;j<509;j++){ 58 tr[i][j] = C(i,j); 59 } 60 } 61 } 62 int main(){ 63 calJc(); 64 calni(); 65 init(); 66 //cout<<"OK"<<endl; 67 cin>>T; 68 while(T--){ 69 LL a,b; 70 scanf("%lld%lld",&a,&b); 71 LL ans = 0; 72 if(a < 500){ 73 for(int i=0;i<=b;i++) 74 ans = (ans + tr[a][i])%mod; 75 } 76 else{ 77 LL t1 = a / 500; 78 LL t2 = a - t1*500; 79 for(int i=0;i<=min(b,t2);i++){ 80 ans = (ans + tr[t2][i]*tran[t1][min(b - i,t1*500)])%mod; 81 } 82 } 83 printf("%lld\n",ans); 84 85 } 86 return 0; 87 }