传送门
这题比赛的时候没怎么认真想,俩队友彷佛在这道题上花了很长时间,但遗憾的是也没搞出来,遂觉得是一道难题。
但正解竟出奇的简单。
若是一对盒子,第一个能够选\(k*i\)个,第二个能够选\(0\sim i-1\)个,那么任意的数均可以由这一对盒子选出来,并且方案惟一。这样就变成了\(x_1+x_2+\cdots+x_n=m\)的非负整数解个数了,用插板法便可解决。php
但如今每一对盒子中的第二个能够选\(0 \sim i\)个,那么当选了\(i\)的整数倍时,就会有两种选法,很差办了。c++
因而题解给出了很漂亮的解决办法:咱们让第\(i\)对盒子的第一个,和第\(i-1\)对盒子的第二个配对,即选\(k*i\)和\(0 \sim i-1\)配对,这样剩下的就只有\(k * 1\)和\(0 \sim n\)这两个盒子。因而咱们只要枚举\(0 \sim n-1\)这个盒子选多少个,再用插板法便可解决!git
即\(\sum\limits_{i = 0}^{n}C_{m-i+n-1}^{n-1}\).ide
将上述式子进行化简,获得\(\sum\limits_{x = m-1}^{m+n-1}C_{x}^{n-1}=\sum\limits_{x=0}^{m+n-1} C_{x}^{n-1}-\sum\limits_{x=0}^{n-2}C_{x}^{n-1}\).函数
进而用\(\textrm{Hockey-Stick Identity}\)(\(C_{n}^{k+1}=\sum\limits_{i=k}^{n-1} C_{i}^k\))化简,最终获得\(C_{n+m}^{n}-C_{m-1}^n\).ui
因此预处理\(O(n)\),单次询问\(O(1)\).
更新spa
最近增强了生成函数,在此也给出生成函数的作法吧:code
对于第\(i\)对盒子,第一个能拿\(ki\)个,那么对应的生成函数就是\(f_{i}(x) = \sum\limits_{n=0}^{\infty} x^{ni}=\frac1{1-x^{i}}\),第二个能拿\(0 \sim i\)个,对应的生成函数就是\(g_{i}(x) = \sum\limits_{n=0}^{i} x^n=\frac{1-x^{n+1}}{1-x}\).get
那么答案对应的多项式就是\(\prod\limits_{i=0}^n f_i(x)*g_i(x)=\frac1{1-x}*(1+x) * \frac1{1-x^2} * \frac{1-x^3}{1-x} \cdots \frac1{1-x^{n-1}} * \frac{1-x^n}{1-x} * \frac1{1-x^n} * \frac{1-x^{n+1}}{1-x}=\frac{1-x^{n+1}}{(1-x)^{n+1}}.\)it
因而咱们将\(\frac{1-x^{n+1}}{(1-x)^{n+1}}\)用广义二项式定理展开,获得
那么当\(m<n+1\)时,\(m\)次项的系数就是\(C_{n+m}^m\);
当\(m \geqslant n + 1\)时,\(m\)次项的系数是\(C_{n+m}^m - C_{m-1}^n\).
#include<bits/stdc++.h> using namespace std; #define enter puts("") #define space putchar(' ') #define In inline typedef long long ll; typedef double db; const int maxn = 2e6 + 5; const ll mod = 1e9 + 7; In ll read() { ll ans = 0; char ch = getchar(), las = ' '; while(!isdigit(ch)) las = ch, ch = getchar(); while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar(); if(las == '-') ans = -ans; return ans; } In void write(ll x) { if(x < 0) x = -x, putchar('-'); if(x >= 10) write(x / 10); putchar(x % 10 + '0'); } ll f[maxn], inv[maxn]; In ll C(int n, int m) { if(m > n) return 0; return f[n] * inv[m] % mod * inv[n - m] % mod; } In ll quickpow(ll a, ll b) { ll ret = 1; for(; b; b >>= 1, a = a * a % mod) if(b & 1) ret = ret * a % mod; return ret; } int main() { f[0] = inv[0] = 1; for(int i = 1; i < maxn; ++i) f[i] = f[i - 1] * i % mod; inv[maxn - 1] = quickpow(f[maxn - 1], mod - 2); for(int i = maxn - 2; i; --i) inv[i] = inv[i + 1] * (i + 1) % mod; int T = read(); while(T--) { int n = read(), m = read(); write((C(n + m, n) - C(m - 1, n) + mod) % mod), enter; } return 0; }