HDU 6333.Problem B. Harvest of Apples-组合数C(n,0)到C(n,m)求和-组合数学(逆元)+莫队 ((2018 Multi-University Training Contest 4 1002))
2018 Multi-University Training Contest 4
6333.Problem B. Harvest of Apples
题意很好懂,就是组合数求和。
官方题解:
我来叨叨一些东西。
这题肯定不能一个一个遍历求和,这样就上天了。。。
解释一下官方题解的意思。
为什么 sum(n,m)=2*sum(n-1,m)-c(n-1,m)。
因为c(n,m)=c(n-1,m)+c(n-1,m-1),至于为什么成立,不懂的百度一下组合数和杨辉三角吧。。。
sum(n,m)=c(n,0)+c(n-1,1)+c(n-1,0)+c(n-1,2)+c(n-1,1)+...+c(n-1,m)+c(n-1,m-1)//因为c(n,0)=c(n-1,0)==1
=c(n-1,0)+c(n-1,0)+c(n-1,1)+c(n-1,1)+c(n-1,2)+c(n-1,2)+...+c(n-1,m-1)+c(n-1,m-1)+c(n-1,m)
=2*sum(n-1,m)-c(n-1,m)
OK,解释完了,然后怎么做呢?
通过上面推出来的公式,我们就可以在O(1)的复杂度里由推出。这个就可以用莫队写了。
关于莫队,具体的去看别人的博客,人家写的很好,我语文不好+懒,不想写。。。
要注意莫队的时候,while里的判断条件是当前指针对应的值与要求得的数的大小的关系,while(N<que[i].n) res=(2*res-C(N++,M)+mod)%mod;就假设,我当前的指针对应的值为sum(n-1,m),我需要得到的结果为sum(n,m),当前的N为n-1,所以我需要用公式sum(n,m)=2*sum(n-1,m)-c(n-1,m),经过这个操作,N就变成n了(因为N++)。
大体就这些,其他的就是关于组合数求解的东西了,这些代码里注释了。
代码:
1 //1002-6333-组合数C(n,0)到C(n,m)求和-组合数学+莫队 2 #include<iostream> 3 #include<cstdio> 4 #include<cstring> 5 #include<algorithm> 6 #include<bitset> 7 #include<cassert> 8 #include<cctype> 9 #include<cmath> 10 #include<cstdlib> 11 #include<ctime> 12 #include<deque> 13 #include<iomanip> 14 #include<list> 15 #include<map> 16 #include<queue> 17 #include<set> 18 #include<stack> 19 #include<vector> 20 using namespace std; 21 typedef long long ll; 22 23 const double PI=acos(-1.0); 24 const double eps=1e-6; 25 const ll mod=1e9+7; 26 const int inf=0x3f3f3f3f; 27 const int maxn=1e5+10; 28 #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); 29 30 int pos[maxn]; 31 ll inv[maxn],f[maxn],ans[maxn]; 32 33 struct node{ 34 int n,m,id; 35 36 bool operator <(const node &a) const{ 37 if(pos[n]!=pos[a.n]) return n<a.n; 38 return m<a.m; 39 } 40 41 }que[maxn]; 42 /* 43 关于逆元 44 费马小定理:对于a和素数p,a^(p-1)恒等于1。 45 逆元的定义:对于正整数a和m,如果有a*x恒等于1,那么把这个同余方程中x的最小正整数解叫做a模m的逆元。一般用欧几里得扩展来做:ax+by=1;称a和b互为逆元 46 a^(p−1)=a^(p−2)∗a,所以有a^(p−2)∗a%p≡1,对比逆元的定义可得,a^(p−2)是a的逆元 47 所以组合数预处理------>阶乘逆元,n!%mod/[(n-m)!%mod*m!%mod],设n!%mod为A,(n-m)!%mod的逆元为B,m!%mod的逆元为C,所以组合数c(n,m)%mod=A*B%mod*C%mod,就酱~ 48 */ 49 ll qpow(ll a,ll b)//快速幂a^b%mod 50 { 51 ll res=1; 52 while(b){ 53 if(b&1) res=(res*a)%mod; 54 a=(a*a)%mod; 55 b>>=1; 56 } 57 return res; 58 } 59 60 void init() 61 { 62 f[1]=1; 63 for(int i=2;i<maxn;i++)//预处理出来i!%mod,就是c(n,m)中n!%mod先预处理 64 f[i]=(f[i-1]*i)%mod; 65 for(int i=1;i<maxn;i++) 66 inv[i]=qpow(f[i],mod-2);//inv中存的是逆元为a^(p-2) 67 } 68 69 ll C(int n,int m)//求C(n,m) 70 { 71 if(n<0||m<0||m>n) return 0; 72 if(m==0||m==n) return 1; 73 return f[n]*inv[n-m]%mod*inv[m]%mod;//就是A*B%mod*C%mod 74 } 75 76 ll res=1; 77 78 int main() 79 { 80 init(); 81 int T; 82 scanf("%d",&T); 83 int block=(int)sqrt(maxn); 84 for(int i=1;i<=maxn-1;i++) 85 pos[i]=(i-1)/block; 86 for(int i=1;i<=T;i++){ 87 scanf("%d%d",&que[i].n,&que[i].m); 88 que[i].id=i; 89 } 90 sort(que+1,que+1+T); 91 int N=1,M=0; 92 for(int i=1;i<=T;i++){ 93 while(N<que[i].n) res=(2*res-C(N++,M)+mod)%mod; 94 while(N>que[i].n) res=((res+C(--N,M))*inv[2])%mod; 95 while(M<que[i].m) res=(res+C(N,++M))%mod; 96 while(M>que[i].m) res=(res-C(N,M--)+mod)%mod; 97 ans[que[i].id]=res; 98 } 99 for(int i=1;i<=T;i++){ 100 printf("%lld\n",ans[i]); 101 } 102 return 0; 103 }