bzoj 4403: 序列统计 (Lucas定理)

题目描述

传送门

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

题解

设m=r-l+1,那么我们相当于m个数,每个数选择xi个,使最后的总和为N
x1+x2+…+xm=N 这其实就相当于N个球放到M个盒子中,允许出现空盒子的方案数。
利用插板法不难得到答案就是C(N+m-1,m-1)
那么这道题的答案就是 ans=Ni=1C(i+m1,m1)
t=m1 ,那么 ans=C(t+1,t)+C(t+2,t)+...+C(t+n,t)
我们强行加入 C(t+1,t+1) ,将式子进行化简。发现 C(t+1,t)+C(t+1,t+1)=C(t+2,t+1) ,然后就可以一项一项的向后合并,最后得到 C(t+n+1,t+1)
那么答案就是 C(t+n+1,t+1)C(t+1,t+1)=C(t+n+1,t+1)1

代码

#include
#include
#include
#include
#include
#define LL long long 
#define p 1000003
#define N 2000000
using namespace std;
int jc[N],inv[N];
int T,n,l,r,m;
int quickpow(LL num,int x)
{
    LL base=num%p; LL ans=1;
    while (x){
        if (x&1) ans=ans*base%p;
        x>>=1;
        base=base*base%p;
    }
    return (int)ans;
}
LL C(LL n,LL m)
{
    if (m>n) return 0;
    return (LL)jc[n]*quickpow(jc[m],p-2)%p*quickpow(jc[n-m],p-2)%p;
}
LL calc(LL n,LL m)
{
    if (m==0) return 1;
    return calc(n/p,m/p)*C(n%p,m%p)%p;
}
int main()
{
    freopen("a.in","r",stdin);
    scanf("%d",&T);
    jc[0]=1;
    for (int i=1;i<=p;i++) jc[i]=(LL)jc[i-1]*i%p;
    //for (int i=0;i<=p;i++) inv[i]=quickpow(jc[i],p-2);
    while (T--) {
        scanf("%d%d%d",&n,&l,&r);
        m=(r-l+1)-1;
        //cout<
        LL ans=calc(m+n+1,m+1)-1;
        ans=(ans%p+p)%p;
        printf("%lld\n",ans);
    }
}

你可能感兴趣的:(数论,组合数取模,组合数学)