LOJ - 10235. 「一本通 6.6 练习 6」序列统计(组合数学+Lucas定理)


题目大意

给定三个正整数 N , L , R N, L, R N,L,R,统计长度在 1 1 1 N N N 之间,元素大小都在 L L L R R R 之间的单调不降序列的数量。输出答案对 1 e 6 + 3 1e6 + 3 1e6+3 取模的结果。

解题思路

假设是从 n n n 个数中选出 m m m 个数,套用经典的计数模型:

若组成单调递增序列,当我们选择了第一个数的时候,后面有 n − 1 n - 1 n1 种选择,选择第二个数的时候,后面有 n − 2 n - 2 n2 种选择…这样最终的方案数目是 n ∗ ( n − 1 ) ∗ . . . ∗ ( n − m + 1 ) = C n m n*(n - 1) *...*(n - m + 1) = C_n^m n(n1)...(nm+1)=Cnm。但是如果组成单调不降序列,选择第一个数时后面实际上有 n + m − 1 n + m - 1 n+m1 种数可以选择(加上了 m − 1 m-1 m1 个可重复的,选择第二个数时后面有 n + m − 2 n + m - 2 n+m2 个数可以选择…这样最终的方案数目显然是 C n + m − 1 m C_{n + m - 1}^m Cn+m1m

综上,设 m = R − L + 1 m = R-L+1 m=RL+1,则本题的答案是 C m 1 + C m 2 + . . . + C m + n − 1 n C_{m}^1 + C_m^2 + ... + C_{m + n - 1}^n Cm1+Cm2+...+Cm+n1n,由于范围过大显然需要化简为一个公式:

C m 1 + C m 2 + . . . + C m + n − 1 n = C m + n n − 1 C_{m}^1 + C_m^2 + ... + C_{m + n - 1}^n = C_{m + n} ^n - 1 Cm1+Cm2+...+Cm+n1n=Cm+nn1

证明:将这 n n n 个组合数前面几项写在纸上,发现它是杨辉三角的一个对角线,根据 C n m = C n − 1 m + C n − 1 m − 1 C_n^m=C_{n-1}^m+C_{n-1}^{m-1} Cnm=Cn1m+Cn1m1,先加上一个 C m 0 C_m^0 Cm0 然后减一,这样两两向下递推可以不断约项,最后得到 C m + n n − 1 C_{m+n}^n - 1 Cm+nn1

#include <bits/stdc++.h>
using namespace std;
#define ENDL "\n"
typedef long long ll;
const int maxn = 1e6 + 10;
const int Mod = 1e6 + 3;

ll qkp(ll x, ll n, ll p) {
    
    
    ll ans = 1;
    x %= p;
    while (n) {
    
    
        if (n & 1) ans = ans * x % p;
        x = x * x % p;
        n >>= 1;
    }
    return ans;
}

ll inv(ll x, ll p) {
    
      //求逆元
    return qkp(x, p - 2, p);
}

ll cal(ll n, ll m, ll p) {
    
    
    if (m > n) return 0;
    ll u = 1, d = 1;
    for (int i = n - m + 1; i <= n; i++) u = u * i % p;
    for (int i = 1; i <= m; i++) d = d * i % p;
    return u * inv(d, p) % p;
}

ll lucas(ll n, ll m, ll p) {
    
    
    if (!m) return 1;
    return cal(n % p, m % p, p) * lucas(n / p, m / p, p) % p;
}

int main() {
    
    
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int T;
    ll n, l, r;
    cin >> T;
    while(T--) {
    
    
        cin >> n >> l >> r;
        ll m = r - l + 1;
        cout << (lucas(n + m, n, Mod) - 1 + Mod) % Mod << endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44691917/article/details/121480529
今日推荐