【題解】CF#896 D-Nephren Runs a Cinema

  容易發現這些 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;
}
相關文章
相關標籤/搜索