不幸的我们并没能做出这题,还被别人在四小时五十八分绝杀.
题意
有 2 n 2n 2n个桶,其中 n n n个里面第 i i i个里面拿出来的球的数量必须是 i i i的倍数,另 n n n个桶里第 i i i个拿出来的球的数量不能超过 n n n,问拿出 m m m个球有多少种方法.对每一个桶都可以拿出 0 0 0个球.
题解
先转换一下题意.
对于两个编号为
i
i
i的桶,我们考虑能拿出
i
i
i的倍数个球的桶和最多拿出
i
−
1
i-1
i−1个球的桶,把这两个桶合成一个桶,惊人的发现这个桶可以取出任意数量的球,并且只有一种取法.
第二个桶还多出一个球,那么整道题可以转换为
n
n
n个能取任意球的桶和
1
1
1个最多能取
n
n
n个球的桶.
枚举在最后一个桶中取
x
x
x个球,则由隔板法可以轻松求出在剩下的桶中取
m
−
x
m-x
m−x个球的方法总数
C
m
+
n
−
x
−
1
n
−
1
C_{m+n-x-1}^{n-1}
Cm+n−x−1n−1.
再用组合计数公式化简
∑
i
=
0
n
C
m
+
n
−
i
−
1
n
−
1
=
C
n
+
m
m
−
C
m
−
1
n
\sum_{i=0}^{n}C_{m+n-i-1}^{n-1}=C_{n+m}^{m}-C_{m-1}^n
∑i=0nCm+n−i−1n−1=Cn+mm−Cm−1n,通过预处理阶乘逆元即可
O
(
1
)
O(1)
O(1)回答询问.
这个化简比较简单的吧,利用
C
n
m
=
C
n
−
1
m
−
1
+
C
n
−
1
m
C_n^m=C^{m-1}_{n-1}+C^{m}_{n-1}
Cnm=Cn−1m−1+Cn−1m
两边同时加上
C
m
−
1
n
C^n_{m-1}
Cm−1n不断消去即可得到.
const int yuzu=2e6,mod=1e9+7;
typedef ll fuko[yuzu|10];
fuko jic={1},inv;
ll kasumi(ll a,ll b=mod-2) {
ll zw=1;
for (;b;b>>=1,a=a*a%mod) if (b&1) zw=zw*a%mod;
return zw;
}
ll zhe(int n,int m) {
return m>n?0:jic[n]*inv[m]%mod*inv[n-m]%mod;
}
int main() {
int i,t,n,m;
for (i=1;i<=yuzu;++i) jic[i]=jic[i-1]*i%mod;
inv[yuzu]=kasumi(jic[yuzu]);
for (i=yuzu-1;~i;--i) inv[i]=inv[i+1]*(i+1)%mod;
for (scanf("%d",&t);t--;) {
scanf("%d%d",&n,&m);
printf("%lld\n",(zhe(n+m,m)-zhe(m-1,n)+mod)%mod);
}
}
谢谢大家.