BZOJ4403: 序列统计

本文解析了BZOJ4403题目的求解思路,通过转换非降序列问题为严格上升序列问题,并利用组合数学中的Lucas定理进行高效计算。讨论了两种逆元预处理的方法及其对程序运行时间的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

BZOJ4403

求非降序列可以考虑将每一个位置加上i,变成求严格上升序列。
那么长度为 i ,范围在lr之间的方案数为 Cii+rl
M=rl+1

ans=i=1nCiM+i1
=C1M+C2M+1+C3M+2++CNM+N1
=1+C0M+C1M+C2M+1+C3M+2++CNM+N1
=CNM+N1

因为 n,m 较大,用 Lucas 定理解决。

如果要预处理逆元,要用线性方法。。试着用费马小定理预处理T掉了。
因为T只有100,每次计算也只有log级别,所以不预处理逆元会更快。

【代码】

不预处理。每次直接算逆元。 360ms

#include <cstdio>
#include <iostream>
#include <algorithm>
#define N 1000000
#define INF 0x7fffffff
#define mod 1000003
using namespace std;
typedef long long ll;
typedef pair<int,int> pa;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}

int T,n,m,l,r;
int Fac[N];

int Qpow(int x,int y)
{
    int rtn=1;
    while(y){
        if(y&1) rtn=1LL*x*rtn%mod;
        x=1LL*x*x%mod;y>>=1;
    }
    return rtn;
}

int Lucas(int x,int y)
{
    if(x<y) return 0;
    if(x<mod&&y<mod) return 1LL*Fac[x]*Qpow(1LL*Fac[y]*Fac[x-y]%mod,mod-2)%mod;
    return 1LL*Lucas(x/mod,y/mod)*Lucas(x%mod,y%mod)%mod;
}

int main()
{
    T=read();
    Fac[0]=1;
    for(register int i=1;i<mod;i++) Fac[i]=(1LL*Fac[i-1]*i)%mod;
    while(T--)
    {
        n=read(),l=read(),r=read();
        m=r-l+1;
        printf("%d\n",(Lucas(n+m,n)-1+mod)%mod);
    }
    return 0;
}

线性预处理逆元 1272ms


#include <cstdio>
#include <iostream>
#include <algorithm>
#define N 1000005
#define INF 0x7fffffff
#define mod 1000003
using namespace std;
typedef long long ll;
typedef pair<int,int> pa;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}

int T,n,m,l,r;
ll Inv[N],Fac[N];

ll Qpow(int x,int y)
{
    ll rtn=1;
    while(y){
        if(y&1) rtn=1LL*x*rtn%mod;
        x=1LL*x*x%mod;y>>=1;
    }
    return rtn;
}

void Pre_Work()
{
    Inv[0]=Fac[0]=Inv[1]=1;
    for(int i=1;i<mod;i++) Fac[i]=(Fac[i-1]*i)%mod;
    for(int i=2;i<mod;i++) Inv[i]=(mod-mod/i)*Inv[mod%i]%mod;
    for(int i=1;i<mod;i++) Inv[i]=(Inv[i]*Inv[i-1])%mod;
}

ll Lucas(int x,int y)
{
    if(x<y) return 0;
    if(x<mod&&y<mod) return Fac[x]*Inv[y]%mod*Inv[x-y]%mod;
    return Lucas(x/mod,y/mod)*Lucas(x%mod,y%mod)%mod;
}

int main()
{
    T=read();
    Pre_Work();
    while(T--)
    {
        n=read(),l=read(),r=read();
        m=r-l+1;
        printf("%lld\n",(Lucas(n+m,n)-1+mod)%mod);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值