4403: 序列统计 组合数学+Lucas定理

组合的知识大大欠缺啊。。好好学习一下。
先膜出题人 yts1999 大爷。
首先转化一下问题:
求长度在 1 N 之间,元素大小都在 L R 之间的单调不降序列的数量。我们进行加标号的操作,将每一位的值都 +i ,即转化为:求长度在 1 N 之间,元素大小都在 L+1 R+N 之间的单调递增序列的数量(不懂画个图)。
那么这个问题就相当于在区间 [L+1,R+N] RL+N 个数中选择 N 个的方案数,即 CNRL+N CRLRL+N
为了表述方便,我们令 M=RL+1 ,上式转化为 CM1M+N1
所以答案为

i=1NCM1i+M1

因为
CMN=CMN1+CM1N1

所以我们有以下等价转化:
Ni=1CM1i+M1
=(Ni=1CM1i+M1)+CMM1
=(Ni=2CM1i+M1)+CMM+11
=(Ni=3CM1i+M1)+CMM+21
...
=CMN+M1
然后用 Lucas 定理解决。

#include<iostream>
#include<cstdio>
#define ll long long 
#define M 1000003
using namespace std;
ll fac[M],inv[M];
inline int read()
{
    int a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
inline void pre()
{
    fac[0]=inv[1]=inv[0]=1;
    for (int i=1;i<M;i++)
        fac[i]=fac[i-1]*i%M;
    for (int i=2;i<M;i++)
        inv[i]=(M-M/i)*inv[M%i]%M;
    for (int i=1;i<M;i++)
        inv[i]=inv[i]*inv[i-1]%M;
}
ll C(int n,int m)
{
    if (n<m) return 0;
    if (n<M&&m<M)
        return fac[n]*inv[m]%M*inv[n-m]%M;
    return C(n/M,m/M)*C(n%M,m%M)%M;
}
int main()
{
    int T=read();
    pre();
    while (T--)
    {
        int n=read(),l=read(),r=read(),m=r-l+1;
        printf("%d\n",(C(n+m,m)+M-1)%M);
    }
    return 0;
}

你可能感兴趣的:(4403: 序列统计 组合数学+Lucas定理)