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 取模的结果。

解题思路

思路一

m = R − L + 1 m=R-L+1 m=RL+1,那么问题可以转化为 m m m 个盒子, n n n 个小球放入其中,盒子可以为空。此时采用隔板法, m − 1 m-1 m1 个隔板即可将小球分割为 m m m 份;先将隔板视为小球,然后分为 m m m 份(每份最少一个)接着每一份里面拿走一个,就是该问题的等价情况。不难发现对应的是 n + m − 1 n+m-1 n+m1 个小球分为 m m m 份,每份最少一个的方案数,即为 C n + m − 1 n C_{n+m-1}^n Cn+m1n

思路二

假设是从 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 + 1 2 + C m + 2 3 + . . . + C m + n − 1 n C_{m}^1 + C_{m+1}^2 +C_{m+2}^3+ ... + C_{m + n - 1}^n Cm1+Cm+12+Cm+23+...+Cm+n1n,由于范围过大显然需要化简为一个公式:

C m 1 + C m + 1 2 + C m + 2 3 + . . . + C m + n − 1 n = C m + n n − 1 C_{m}^1 + C_{m+1}^2 + C_{m+2}^3+... + C_{m + n - 1}^n = C_{m + n} ^n - 1 Cm1+Cm+12+Cm+23+...+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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值