容易發現這些 vip 用戶並沒什麼用,因此考慮枚舉手持50元與100元的人共有多少個。設手持50元的人 \(a\) 個,手持100元的人 \(a - k\) 個,那麼一共是 \(2*a - k\) 我的,最後手上會剩餘 \(k\) 張50元鈔票。用卡特蘭數計算獲得在這種狀況下的方案數就是:c++
\((\binom{2 * a - k}{a} - \binom{2 * a - k}{a + 1}) * \binom{n}{2 * a - k}\)spa
其中 \(l <= k <= r, 1 <= 2 * a - k <= n\)。code
把組合數分開計算,獲得答案實際上是兩部分的和:blog
\((\binom{2 * a - k}{a} * \binom{n}{2 * a - k}\)ip
與 \(-\binom{2 * a - k}{a + 1} * \binom{n}{ 2 * a - k}\)get
注意到這兩個式子都可以化簡\(\binom{n}{m} * \binom{m}{r} = \binom{n}{r} * \binom{n - m}{m - r}\),it
獲得:class
\(ans = \binom{n}{a}\binom{n - a}{a - k} - \binom{n}{a + 1} * \binom{n - a - 1}{a - k - 1}\)擴展
分開計算兩個部分的和,發現上部分是在計算 \(\sum \binom{n}{a}\binom{n - a}{a - k} (a\in [1, n], k \in [l, r])\)im
而下部分則是在計算 \(\sum \binom{n}{a}\binom{n - a}{a - k - 2} (a\in [2, n + 1], k \in [l, r])\)
把中間一樣的部分約去便可獲得一個 \(O(n)\) 的式子,每一步需求解一個組合數。
而後問題是怎樣求出 \(n\) 個對合數取模的組合數?常見思路利用擴展盧卡斯已經不可行,但鑑於此題 \(n\) 比較小,咱們能夠暴力拆分組合數中的階乘數。對於與模數互質的部分利用歐拉定理求出逆元,照常處理;不互質的則計算上下約去了多少個質因子後暴力累乘貢獻。
#include <bits/stdc++.h> using namespace std; #define maxn 200000 #define CNST 30 #define int long long int n, P, L, R, ans, cnt, fac[maxn], finv[maxn]; int tot, a[maxn], num[maxn][CNST]; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } void Up(int &x, int y) { x = (x + y) % P; if(x < 0) x += P; } int Qpow(int x, int timer, int P) { int base = 1; for(; timer; timer >>= 1, x = x * x % P) if(timer & 1) base = base * x % P; return base; } void Pre(int n) { int x = P, phi = P; fac[0] = finv[0] = 1; for(int i = 2; i * i <= x; i ++) if(x % i) continue; else { phi = phi / i * (i - 1); a[++ cnt] = i; while(!(x % i)) x /= i; } if(x > 1) phi = phi / x * (x - 1), a[++ cnt] = x; for(int i = 1; i <= n; i ++) { int t = i; for(int j = 1; j <= cnt; j ++) { num[i][j] = num[i - 1][j]; while(!(t % a[j])) t /= a[j], num[i][j] ++; } fac[i] = fac[i - 1] * t % P; finv[i] = Qpow(fac[i], phi - 1, P); } } int Get_C(int n, int m) { int ret = fac[n] * finv[m] % P * finv[n - m] % P, x, y; if(n < 0 || m < 0 || n < m) return 0; for(int i = 1; i <= cnt; i ++) { if(a[i] > n) break; int cnt = num[n][i] - num[m][i] - num[n - m][i]; ret = ret * Qpow(a[i], cnt, P) % P; } return ret; } signed main() { n = read(), P = read(), L = read(), R = read(); Pre(n + 5); for(int i = 0; i <= n; i ++) { if(L > i || R < 0) continue; int l = max(L, 0LL), r = min(i, R), t = Get_C(n, i); Up(ans, P - t * Get_C(n - i + 1, i - r - 1) % P); Up(ans, t * Get_C(n - i + 1, i - l) % P); } printf("%I64d\n", ans); return 0; }