AcWing 1312. 序列统计

AcWing 1312. 序列统计

一、题目描述

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

输出答案对 106+3 取模的结果。

输入格式

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

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

输出格式

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

数据范围
0N,L,R109,1T100,输入数据保证 LR

输入样例

2
1 4 5
2 4 5

输出样例:

2
5

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

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

二、算法分析

1、长度是 k 的序列

枚举序列长度k,则对于选出来的元素一定满足L<=a1<=a2<=<=ak<=R ,这样才算是题目要求的单调不降序列

我们更希望可以将 a1<=a2<=<=ak 的形式转化为 b1<b2<<bk的形式,因为后者可以通过隔板法更容易解决

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

b1=a1
b2=a2+1
b3=a3+2
...
bk=ak+k1

则原不等式

L<=a1<=a2<=<=ak<=R

转换成

L<=b1<b2<<bk<=R+k1

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

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

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

由于序列长度是 k 的方案是 CRL+kk,令 RL=m,则 k1,2....N 时的 总方案数

=Cm+11+Cm+22+Cm+33+...+Cm+nn

下面是一个 经典的变形技巧 ,利用公式:
Cab=Ca1b+Ca1b1Cm+10+Cm+11=Cm+21
然后形成多米诺骨牌,顺次形成递推关系,一路向前~

=Cm+10+Cm+11+Cm+22+Cm+33+...+Cm+nnCm+10=Cm+21+Cm+22+Cm+33+...+Cm+nnCm+10...=Cm+n+1nCm+10=Cm+n1n1

题目给定 P=106+3,N=109,因此只能用卢卡斯定理解决

时间复杂度 O(PlogpN)

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

三、实现代码

#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 @   糖豆爸爸  阅读(73)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2018-06-08 东师理想云平台异步任务处理系统V2.0重构思路
2018-06-08 Mysql查询出所有列名
2018-06-08 IntelliJ IDEA添加jar包
2017-06-08 Mysql中使用存储过程返回查询多个表的数据信息
Live2D
点击右上角即可分享
微信分享提示