HDU7047 Link with Balls
阿新 • • 發佈:2021-08-11
傳送門
這題比賽的時候沒怎麼認真想,倆隊友似乎在這道題上花了很長時間,但遺憾的是也沒搞出來,遂以為是一道難題。
但正解竟出奇的簡單。
如果一對盒子,第一個可以選\(k*i\)個,第二個可以選\(0\sim i-1\)個,那麼任意的數都可以由這一對盒子選出來,而且方案唯一。這樣就變成了\(x_1+x_2+\cdots+x_n=m\)的非負整數解個數了,用插板法即可解決。
但現在每一對盒子中的第二個可以選\(0 \sim i\)個,那麼當選了\(i\)的整數倍時,就會有兩種選法,不好辦了。
於是題解給出了很漂亮的解決辦法:我們讓第\(i\)對盒子的第一個,和第\(i-1\)對盒子的第二個配對,即選\(k*i\)
即\(\sum\limits_{i = 0}^{n}C_{m-i+n-1}^{n-1}\).
將上述式子進行化簡,得到\(\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}\)
所以預處理\(O(n)\),單次詢問\(O(1)\).
#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; }