小奇的赏花(数学排列组合)

                                                   小奇的赏花

题目描述

小奇的花园里有n行m列棵桃花树,花色各不相同。小奇漫步在花园中,有时它觉得某一行/列的桃花很美,便会在这一整行/列的每棵树下捡一枚花瓣,到了傍晚,他发现自己选择了r行c列(同一行/列可能被选择不止一次)的花瓣。

回家之后,小奇发现:有s种颜色的花瓣数为奇数,他想知道,有多少种选择方案能有这样的效果呢?(两种方案不同当且仅当某行/列被选择的次数不同)

输入格式

第一行包括5个整数,n,m,r,c,s。

输出格式

输出一个整数表示答案(mod 1000000007)。

样例

样例输入

2 2 2 2 4

样例输出

4

数据范围与提示

对于 20% 的数据, n,m ≤ 4,r,c ≤ 4;

对于 50% 的数据, n,m ≤ 500,r,c ≤ 2000;

对于另外10% 的数据, n,m ≤ 100000,s = n * m;

对于 100% 的数据, n,m ≤ 100000,r,c ≤ 100000,s ≤ 10^12。

思路:这就是一道裸的数学排列组合题,设有x行被选择奇数次,有y列被选择奇数次,因为奇+偶=奇,奇+奇=偶,偶+偶=偶,而题中s为奇数,则可列出这样的等式:x*(m-y)+y*(n-x)=s,解得:y=(s-x*m)/(n-2*x),枚举x即可得出y,用每对x,y计算种类再加和,即为答案(分步加法)。那怎样计算每对x,y对应的种类数呢,当然用组合数求解。

对于每对x,y而题目中限制只有r次,问题可转化成把r个苹果分到n个盘子中,保证有x个奇数盘子,先把x个苹果分别放到x个盘子中,方案数为Cxn,之后剩下(r-x)个苹果,把这些苹果分成(r-x)/2对放到n个盘子中允许为空,用隔板法解决。防止(r-x)/2对苹果填不满n个盘子,加上n个盘子,则有(r-x)/2对苹果和n个空分成n份放入n个不同的盘子(可以理解成有序)一共有(r-x)/2+n-1个空,插入n-1个隔板,方案数为Cn-1(r-x)/2+n-1    ,同理y也如此,则对每对x,y方案数为Cxn*Cn-1(r-x)/2+n-1*Cym*Cm-1(c-y)/2+m-1

方案数用逆元求即可。

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 typedef long long ll;
 5 const int mod=1000000007,maxn=200000+10;
 6 ll r,c,n,m;
 7 ll f[maxn],fac[maxn],ine[maxn];
 8 ll s;
 9 void Init(){
10     fac[0]=1;
11     for(int i=1;i<=200000;i++) fac[i]=fac[i-1]*i%mod;//求阶乘
12     ine[1]=1;
13     for(int i=2;i<=200000;i++){
14         ll a=mod/i;
15         ll b=mod%i;
16         ine[i]=(-a*ine[b]%mod+mod)%mod;//线性求逆元
17     }
18     f[0]=1;
19     for(int i=1;i<=200000;i++) f[i]=f[i-1]*ine[i]%mod;//逆元的阶乘
20 }
21 ll cmb(ll x,ll y){
22     ll cnt=1;
23     cnt=cnt*f[x]%mod;
24     cnt=cnt*f[y-x]%mod;
25     cnt=fac[y]*cnt%mod;
26     return cnt;
27 }//组合数
28 ll cal(int x,int y){
29     if((r-x)%2||(c-y)%2) return 0;//当(r-x)/2或(c-y)/2不为整数时,不成立,因为若(r-x)为奇数,它分出若干份偶数个苹果,最后肯定剩下一个苹果,这个苹果若放在
30     ll cnt=1;                     //那x个盘子中则有一个盘子不为奇数,若放在其余盘子中,则多一个盘子为奇数
31     cnt=cnt*cmb(x,n)%mod;
32     cnt=cnt*cmb(n-1,(r-x)/2+n-1)%mod;
33     cnt=cnt*cmb(y,m)%mod;
34     cnt=cnt*cmb(m-1,(c-y)/2+m-1)%mod;
35     return cnt;
36 }//每对x,y对应的方案数
37 int main(){
38     scanf("%lld%lld%lld%lld%lld",&n,&m,&r,&c,&s);
39     Init();
40     ll ans=0;
41     for(ll i=0;i<=min(n,r);i++){
42         if(n==2*i){
43             if(s==i*m){
44                 for(ll j=0;j<=min(m,c);j++) ans=(ans+cal(i,j))%mod;
45             }//注意枚举x时当(n==2*i&&s==i*m)此时j可取范围以内的任何值                    
46         }
47         else{
48             if((s-i*m)%(n-2*i)==0){
49                 ll y=(s-i*m)/(n-2*i);
50                 if(y<=min(m,c)&&y>=0) ans=(ans+cal(i,y))%mod;
51             }
52         }
53     }
54     printf("%lld\n",ans);
55     return 0;
56 }
View Code

 

posted @ 2020-07-28 16:45  ddoodd  阅读(222)  评论(0编辑  收藏  举报