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_{i=0}^{m}C(n,i)

官方题解:

我来叨叨一些东西。

这题肯定不能一个一个遍历求和,这样就上天了。。。

解释一下官方题解的意思。

为什么 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)的复杂度里由Sum(n,m)推出Sum(n-1,m),Sum(n+1,m),Sum(n,m-1),Sum(n,m+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 }

 

posted @ 2018-08-05 20:49  ZERO-  阅读(320)  评论(0编辑  收藏  举报