组合数重点整理

来源:互联网 发布:fedora yum 编辑:程序博客网 时间:2024/05/18 01:12

总结下最近刷过的关于组合数的题目,以供以后复习参考。
首先要介绍的就是lucas定理,这也是组合数中重点中的重点。
这里写图片描述
这里写图片描述
同时,因为我们也经常要遇到关于取模的运算,所以关于逆元的概念也必不可少。
这里写图片描述
这里写图片描述
好了,接下来就是通过解决实际问题,来实践了。
1.hdu3037
解析:通过隔板法,求C(n+m , m )% p即可,这里我们就需要用到lucas定理了,注意,我们一般会预处理一些所需要的阶乘运算,以便后来方便使用,降低复杂度,在这道题里面,我们预先处理了0~p的阶乘。

/*ID: CaoLeiPROG: hdu_3037.cppLANG: C++*/#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>#include <set>#include <queue>#include <map>#include <vector>using namespace std;#define N 100010#define pi acos(-1.0)#define inf 100000000typedef long long ll;typedef unsigned long long ull;ll p;ll fact[N];void init(){    fact[0]=1;    for(int i=1;i<=p;i++)      fact[i]=fact[i-1]*i%p;}ll pow(ll a,ll b,ll pp){    ll tmp=a,ans=1;    while(b){        if(b&1){            ans=ans*tmp%pp;        }        b>>=1;        tmp=tmp*tmp%pp;    }    return ans;}ll lucas(ll n,ll m,ll p){    if(m==0) return 1;    else if(n%p<m%p) return 0;    else return fact[n%p]*pow(fact[n%p-m%p]*fact[m%p]%p,p-2,p)%p*lucas(n/p,m/p,p)%p;   //在这里面痛哇,fact[n-m]*fact[m]%p忘了模除}int main(){    freopen("in.txt","r",stdin);    int t;    scanf("%d",&t);    ll n,m;    while(t--){        scanf("%lld%lld%lld",&n,&m,&p);        init();        printf("%lld\n",lucas(n+m,m,p));    }    return 0;}

2.hdu3944
解析:其实将部分答案打出来仔细观察,就可以发现只需要求(C(n+1,m)+n-m)%p即可,但是注意这道题需要预处理大量的答案。

/*ID: CaoLeiPROG: hdu_3944.cppLANG: C++*/#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>#include <set>#include <queue>#include <map>#include <cmath>#include <vector>using namespace std;#define N 10010#define pi acos(-1.0)#define inf 100000000typedef long long ll;typedef unsigned long long ull;ll p;int prime[10010];bool is[10010];int fact[N*1250];ll pow(ll a,ll b,ll pp){    ll tmp=a,ans=1;    while(b){        if(b&1){            ans=ans*tmp%pp;        }        b>>=1;        tmp=tmp*tmp%pp;    }    return ans;}ll getint(){    char ch=getchar();    ll ret=0;    while(ch<'0'||ch>'9')ch=getchar();    while(ch>='0'&&ch<='9'){        ret=ret*10+ch-'0';ch=getchar();    }    return ret;}  ll lucas(ll n,ll m,ll p){    if(m==0) return 1;    else if(n%p<m%p) return 0;    else return (ll)fact[n%p+prime[p]]*pow((ll)fact[n%p-m%p+prime[p]]*fact[m%p+prime[p]]%p,p-2,p)%p*lucas(n/p,m/p,p)%p;   }int main(){    freopen("in.txt","r",stdin);    int po=0;    is[0]=is[1]=true;    for(int i=2;i<=10000;i++){        if(!is[i]){            prime[i]=po*10000;            fact[po*10000]=1;            for(int j=po*10000+1;j<=po*10000+i;j++){                fact[j]=(ll)fact[j-1]*ll(j-po*10000)%(ll)i;            }            po++;            for(int j=2*i;j<=10000;j+=i) is[j]=true;        }    }    ll n,m;    int cnt=1;    while(~scanf("%lld",&n)){        m=getint();        p=getint();        if(m>n/2) m=n-m;          printf("Case #%d: %lld\n",cnt++,(lucas(n+1,m,p)+n-m)%p);    }    return 0;}

3.hdu4349
解析:这时候就要借鉴《初等数论》上的结论了。
这里写图片描述

/*ID: CaoLeiPROG: hdu_4349.cppLANG: C++*/#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>#include <set>#include <queue>#include <map>#include <cmath>#include <vector>using namespace std;#define N 500010#define pi acos(-1.0)#define inf 100000000typedef long long ll;typedef unsigned long long ull;int main(){    int n,k;    while(~scanf("%d%d",&n,&k)){        while(n){            if(n%2==0&&k%2==1) break;            n>>=1;            k>>=1;        }        if(n) printf("0\n");        else printf("1\n");    }    return 0;}

4.poj3146
解析:这道题我按照上一题的结论试了试,就AC了。

#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>#include <set>#include <queue>#include <map>#include <cmath>#include <vector>using namespace std;#define N 1010#define pi acos(-1.0)#define inf 100000000typedef long long ll;typedef unsigned long long ull;int main(){    int p,n,cnt=0;    int ans;    while(~scanf("%d%d",&p,&n)){        if(p==0&&n==0) break;        ans=1;        while(n){            ans*=(n%p+1);            ans%=10000;            n/=p;        }        printf("Case %d: ",++cnt);        if(ans<10) printf("000%d\n",ans);        else if(ans<100) printf("00%d\n",ans);        else if(ans<1000) printf("0%d\n",ans);        else printf("%d\n",ans);    }    return 0;}

5.zoj3557
解析:同样是隔板问题,但是要注意下预处理。

/*ID: CaoLeiPROG: zoj_3557.cppLANG: C++*/#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>#include <set>#include <queue>#include <map>#include <cmath>#include <vector>using namespace std;#define N 500010#define pi acos(-1.0)#define inf 100000000typedef long long ll;typedef unsigned long long ull;ll p;ll fact[N];ll pow(ll a,ll b,ll pp){    ll tmp=a,ans=1;    while(b){        if(b&1){            ans=ans*tmp%pp;        }        b>>=1;        tmp=tmp*tmp%pp;    }    return ans;}ll c(ll n,ll m){    ll a=1;    for(int i=n;i>n-m;i--)      a=a*i%p;    a=a*pow(fact[m]%p,p-2,p)%p;    return a;}ll lucas(ll n,ll m,ll p){    if(m==0) return 1;    else if(n%p<m%p) return 0;    else return c(n%p,m%p)%p*lucas(n/p,m/p,p)%p;   //在这里面痛哇,fact[n-m]*fact[m]%p忘了模除}int main(){    freopen("in.txt","r",stdin);    ll n,m;    while(~scanf("%lld%lld%lld",&n,&m,&p)){        fact[0]=1;        for(int i=1;i<=m;i++){            fact[i]=fact[i-1]*i%p;        }        printf("%lld\n",lucas(n-m+1,m,p));    }    return 0;}

(持续更新中)

0 0