bzoj4403 序列统计 ( 组合数学 + lucas )

bzoj4403 序列统计

原题地址http://www.lydsy.com/JudgeOnline/problem.php?id=4403

题意:
多组数据。
给定三个正整数N、L和R,统计长度在1到N之间,元素大小都在L到R之间的单调不降序列的数量。输出答案对10^6+3取模的结果。

数据范围
1≤N,L,R≤10^9,1≤T≤100

题解:
首先,考虑对于一个长度为n的序列,如何求元素大小都在L到R之间的单调不降序列的数量。

这里提供一个常用转化
此类单调不降问题常常把第i个数加上i,变成求升序序列的问题。
升序序列,即任选出n个不重复的数,答案是comb(值域,n)

因为已经第i个数加上i,值域从[L,R] 变为 [L+1,R+n]
因此,一个长度为n的序列,元素大小都在L到R之间的单调不降序列的数量就是:
comb(R-L+n,n)

原题转化为:
Σni=1CiRL+i
=CnRL+n+Cn1RL+n1+...C2RL+2+C1RL+1
=CnRL+n+Cn1RL+n1+...C2RL+2+C1RL+1+11
=CnRL+n+Cn1RL+n1+...C2RL+2+C1RL+1+C0RL+11
因为 Cmn=Cmn1+Cm1n1
原式 =CnRL+n+Cn1RL+n1+...C2RL+2+C1RL+21
=CnRL+n+Cn1RL+n1+...C2RL+3+1
依次合并,最后得到
CnRL+n+11
直接算即可

(化简式子的问题,添减一项来凑是常用的做法)

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define LL long long
using namespace std;
const int mod=1000003;
const int N=1000010;
int T,n,L,R,fac[N],inv[N];
int C(int n,int m)
{
    if(n<m||n<0||m<0) return 0;
    if(m==0) return 1;
    return ((1LL*(1LL*fac[n]*inv[fac[m]])%mod)*(inv[fac[n-m]]%mod))%mod;
}
int lucas(int n,int m,int p)
{
    if(n<m) return 0;
    if(m==0) return 1;
    return (1LL*(lucas(n/p,m/p,p)%mod)*(C(n%p,m%p)%mod))%mod;
}
void init()
{
    fac[0]=1; inv[1]=1; inv[0]=1;
    for(int i=1;i<=mod;i++)
    {
        fac[i]=(1LL*fac[i-1]*i)%mod;
        if(i==1) continue;
        inv[i]=(1LL*(mod-mod/i)*inv[mod%i])%mod;    
    }
}
int main()
{
    init();
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&L,&R);
        printf("%d\n",(lucas(R-L+n+1,n,mod)-1+mod)%mod);
    }

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值