• 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
    ",ans[i]);
    101     }
    102     return 0;
    103 }
  • 相关阅读:
    CentOS 6.3用ssh无密码登陆其它主机
    堆排序算法以及JAVA实现
    TaskTracker发送Heartbeat以及接受HeartbeatResponse
    Linux常用命令总结
    用eclipse将mapreduce程序打成jar包并在命令行执行
    jquery自定义验证方法
    solaris中几个网络经典命令小结
    DWR的简单总结[转]
    java 编码问题 及转换
    DWR学习详解【转】
  • 原文地址:https://www.cnblogs.com/ZERO-/p/9427107.html
Copyright © 2020-2023  润新知