小奇的赏花(数学排列组合)
小奇的赏花
题目描述
小奇的花园里有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 }