bzoj4403 序列统计 卢卡斯定理

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

题解:
一个新的套路:把单调不降问题转化为对于每个位置,让它的数值加上下标,从而转化成单调上升问题。
转化为单调上升之后,我们把 a i 加上了 i ,那么问题就转化为了在 [ l + 1 , r + n ] 中选 n 个不同的数的方案数。
我们设 r l + 1 m ,那么答案就是

i = 1 n C m + i 1 i
展开后就是
C m 1 + C m + 1 2 + . . . + C m + n 1 n
我们没法直接计算,所以要变形一下,由 C m 0 = 1 把原式变成
1 + C m 0 + C m 1 + C m + 1 2 + . . . + C m + n 1 n
我们发现根据组合数递推式 C n m = C n 1 m 1 + C n 1 m ,这个式子前两项 C m 0 + C m 1 可以合并成 C m + 1 1 ,然后此时 C m + 1 1 C m + 1 2 又可以合并成 C m + 2 2 ,以此类推,我们最后可以把式子合并成 C m + n n 1 ,然后因为要取模,并且模数是质数,所以我们用卢卡斯定理就可以算出 C m + n n ,最后再 1 即可。
代码:

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

int t;
long long n,r,l,fac[1000010],mod=1e6+3;
inline long long ksm(long long x,long long y,long long mod)
{
    long long res=1;
    while(y)
    {
        if(y&1)
        res=(res*x)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return res;
}
inline long long c(long long n,long long m)
{
    if(m>n)
    return 0;
    return fac[n]*ksm((long long)fac[m]*fac[n-m]%mod,mod-2,mod)%mod;
}
inline long long lucas(long long n,long long m)
{
    if(m>n)
    return 0;
    if(m==0)
    return 1;
    return (c(n%mod,m%mod)*lucas(n/mod,m/mod))%mod;
}
int main()
{
    scanf("%d",&t);
    fac[0]=1;
    for(int i=1;i<mod;++i)
    fac[i]=(fac[i-1]*i)%mod;
    while(t--)
    {
        scanf("%lld%lld%lld",&n,&l,&r);
        long long m=r-l+1;
        printf("%lld\n",((lucas(m+n,n)-1)%mod+mod)%mod);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/80826059
今日推荐