题目:
有 n 个不同的气球和 m 个不同的重物,每个气球都可以提供 1N 的升力(竖直向上),每个重物都会受到 1N 的重力(竖直向下)。
现要选出若干个气球和若干个重物,将他们固定在一起,并且使得固定之后的整体受力平衡,请问共有多少种满足条件的方案?
输入格式
输入数据第一行一个正整数 T,表示测试数据组数
接下来 T 行,每行包含两个空格隔开的正整数 n 和 m
输出格式
输出 T 行,每行包括一个数字,表示答案除以 (10^9+7 ) 的余数
数据范围
对于 20% 的数据,m = 1
对于 60% 的数据,1≤ n,m ≤10^3,1≤ T ≤10^4
对于全部的数据,1≤ n,m ≤10^6,1 ≤ T ≤10^6
输出时每行末尾的多余空格,不影响答案正确性
样例输入
1
2 3
样例输出
9
样例解释
不妨用 b1,b2 表示两个气球,用 w1,w2,w3 表示三个重物
下面每行都描述了一个符合要求的方案
b1,w1
b1,w2
b1,w3
b2,w1
b2,w2
b2,w3
b1,b2,w1,w2
b1,b2,w1,w3
b1,b2,w2,w3
题解:
1.先看题目用例,气球2个,重物3个,气球我可以用1个,也可以用2个;如果用1个,有2种选择,此时重物有3种选择,共2*3种;如果用2个,气球只有1种选择,而重物有3种选择,共1*3种;共2*3+1*3=9,如果用组合数表示:2.由此推广到气球n个,重物m个,可得答案为:(假设你n<=m)
3.接下来对其进行化简:(如果通过复杂的数学公式推导,本人水平有限,不会)先将原式改写一下:(用到组合数基本性质:)得到
我们在原本的基础上增加了第一项,保持值不变,我们在最后一项减去,并且我们可以发现这一项的值为1。
即为:
为了求出其值,我们建立一个模型:
假设你现在单身,有来自北方的和南方两种风格的女朋友任君挑选,北方的n个人记为A队,南方的m个人记为B队,由于南方的人口比北方多,可知m>=n;现在让你做出一个艰难的决定,让你从中挑选n个人作为你的女朋友,有多少种选法?(如果你是女生,自动脑补为男朋友)
<1>.有最简单的排列组合知识知有:选法
<2>.从另一种角度来看,你对南方或者北方的风格有侧重,可以从南方选k个,剩下(n-k)个从北方选(k从0到n):
这样得到的结果为:
由<1>,<2>知两个结果是相等的,可推出=
接着推出 =
,也就是(m+n)!/(m!*n!)-1;
(注:这其实来自于一个非常经典的证明:,证明方法是一样的,这个结果非常重要,可以记住)
4.最重要的解决了,现在是代码实现,由上一步结果知我们需要计算(m+n)的阶乘,有多组测试用例,节省时间,我们需要打表实现阶乘,然后又逆元部分(利用快速幂+费马小定理 可参考博客:https://blog.youkuaiyun.com/qq_44616044/article/details/107252092)可得出(m+n)!/(m!*n!)%mod=(m+n)!*pow(m!,mod-2)*pow(n!,,mod-2)%mod,由于数比较大,所以需要每一步mod上mod,则结果为{[(m+n)!*pow(m!,mod-2)]%mod*pow(n!,mod-2)}%mod,然后用快速幂替换幂运算;
AC代码
#include<stdio.h>
#define ll long long
const int mod = 1e9+7;
ll md[2000005]; //n+m
ll quick_pow(ll x,ll n,ll m)
{
ll res = 1;
while(n > 0)
{
if(n & 1)
res = res * x % m;
x = x * x % m;
n >>= 1;
}
return res;
}
int main()
{
ll m,n;
md[0]=1;
for(int i=1;i<=2000000;i++)
{
md[i]=(md[i-1]*i)%mod;//每次%mod,防止超出long long范围,不改变最终结果
}//打表
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);//这题时间卡的特别紧,我用cin关了同步也会超时
ll ans=((md[n+m]*quick_pow(md[n],mod - 2,mod)))%mod*quick_pow(md[m],mod - 2,mod)%mod; //一定要每一步%mod
printf("%lld\n",ans-1);
}
return 0;
}