AcWing 1312. 序列统计

\(AcWing\) \(1312\). 序列统计

一、题目描述

给定三个整数 \(N,L,R\),统计长度在 \(1\)\(N\) 之间,元素大小都在 \(L\)\(R\) 之间的 单调不降序列 的数量。

输出答案对 \(10^6+3\) 取模的结果。

输入格式

输入第一行包含一个整数 \(T\),表示数据组数。

第二到第 \(T+1\) 行每行包含三个整数 \(N,L,R\)

输出格式

输出包含 \(T\) 行,每行有一个数字,表示你所求出的答案对 \(10^6+3\) 取模的结果。

数据范围
\(0≤N,L,R≤10^9,1≤T≤100\),输入数据保证 \(L≤R\)

输入样例

2
1 4 5
2 4 5

输出样例:

2
5

样例解释
对于第一组输入,满足条件的两个序列为 \(\{4\},\{5\}\)

对于第二组输入,满足条件的五个序列为\(\{4\},\{5\},\{4,4\},\{5,5\},\{4,5\}\)

二、算法分析

1、长度是 \(k\) 的序列

枚举序列长度\(k\),则对于选出来的元素一定满足\(L<=a_1<=a_2<=…<=a_k<=R\) ,这样才算是题目要求的单调不降序列

我们更希望可以将 \(a_1<=a_2<=…<=a_k\) 的形式转化为 \(b_1<b_2<…<b_k\)的形式,因为后者可以通过隔板法更容易解决

常用的转换方法:将差值映射变大,令

\(\displaystyle b_1=a_1\)
\(\displaystyle b_2=a_2+1\)
\(\displaystyle b_3=a_3+2\)
...
\(\displaystyle b_k=a_k+k-1\)

则原不等式

\[\large L<=a_1<=a_2<=…<=a_k<=R \]

转换成

\[\large L<=b_1<b_2<…<b_k<=R+k−1 \]

每找到一个 \(a_i\) 都能对应映射成一个 \(b_i\) ,同时每找到一个 \(b_i\) 都能对应映射成一个 \(a_i\),这样构建出来的不等组与原不等式是等价的

因此序列长度是 \(k\) 的方案是 $$\large C_{R+k−1−L+1}k=C_{R−L+k}k$$
但是,由于选择的元素数量\(k\)不确定,取值范围还很大,是 \(10^9\)。暴力枚举\(k\)肯定不行,需要继续想办法处理:

2、计算出长度是\(1,2,3 … N\) 的序列总方案数

由于序列长度是 \(k\) 的方案是 \(C_{R−L+k}^k\),令 \(R−L=m\),则 \(k\)\(1,2....N\) 时的 总方案数

\(\large \displaystyle =C_{m+1}^1+C_{m+2}^2+C_{m+3}^3+...+C_{m+n}^n\)

下面是一个 经典的变形技巧 ,利用公式:
\(\large \displaystyle C_{a}^b=C_{a-1}^{b}+C_{a-1}^{b-1} \Rightarrow C_{m+1}^{0}+C_{m+1}^{1}=C_{m+2}^{1}\)
然后形成多米诺骨牌,顺次形成递推关系,一路向前~

\(\large =C_{m+1}^0 +C_{m+1}^1+C_{m+2}^2+C_{m+3}^3+...+C_{m+n}^n - C_{m+1}^0 \\ =C_{m+2}^1+C_{m+2}^2+C_{m+3}^3+...+C_{m+n}^n - C_{m+1}^0 \\ ... \\ =C_{m+n+1}^n-C_{m+1}^0 \\ =C_{m+n-1}^n-1 \)

题目给定 \(P = 10^6+3, N = 10^9\),因此只能用卢卡斯定理解决

时间复杂度 \(O(Plog_pN)\)

\(Lucas\)定理的时间复杂度是\(O(Plog_pN)\)

三、实现代码

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int p = 1000003;

/**
 * 功能:快速幂模板
 * @param a
 * @param k
 * @param p
 * @return
 */
int qmi(int a, int k, int p) {
    int res = 1;
    while (k) {
        if (k & 1) res = (LL)res * a % p;
        a = (LL)a * a % p;
        k >>= 1;
    }
    return res;
}

/**
 * 功能:组合数模板
 * @param a 在a个数中
 * @param b 取b个数
 * @param p 一个质数,用来取模
 * @return 多少种办法
 */
int C(int a, int b, int p) {
    if (a < b) return 0;
    int down = 1, up = 1;
    for (int i = a, j = 1; j <= b; i--, j++) {
        up = (LL)up * i % p;
        down = (LL)down * j % p;
    }
    return (LL)up * qmi(down, p - 2, p) % p;
}

/**
 * 功能:Lucas公式模板
 * @param a
 * @param b
 * @param p
 * @return
 */
int lucas(LL a, LL b, int p) {
    if (a < p && b < p) return C(a, b, p);
    return (LL)C(a % p, b % p, p) * lucas(a / p, b / p, p) % p; //套用公式,还有个递归
}

int main() {
    int T;
    cin >> T;
    while (T--) {
        int n, l, r;
        cin >> n >> l >> r;
        cout << (lucas(r - l + n + 1, r - l + 1, p) - 1 + p) % p << endl;
    }
    return 0;
}
posted @ 2022-06-08 16:07  糖豆爸爸  阅读(106)  评论(0)    收藏  举报
Live2D