[ 数论 ] Codeforces896D

枚举VIP的个数 x ,求出第一种人个数的范围 [L,R]
用类似求卡特兰数的方法可以得出答案为

(nx)i=LR(nxi)(nxi+1)


(nx)(nxL)(nx)(nxR+1)

预处理组合数时可以先将所有项除以其与 p 的最大公约数,求的时候再乘上去。
时间复杂度 O(nlogploglogp)

#include
using namespace std;
#define N 100010
#define M 33
int i,j,k,n,m,p;
int a[M],c[N][M];
int fac[N],inv[N];
int l,r,L,R,len,Ans;
inline int Pow(int x,int y){
    int Ans=1;
    for(;y;y>>=1,x=1ll*x*x%p)if(y&1)Ans=1ll*Ans*x%p;
    return Ans;
}
inline void Init(){
    int t=p;
    for(int i=2;i*i<=t;i++)
    if(!(t%i)){
        a[++m]=i;t/=i;
        while(!(t%i))t/=i;
    }
    if(t>1)a[++m]=t;
    int phi=p;
    for(int i=1;i<=m;i++)phi=phi/a[i]*(a[i]-1);
    fac[0]=inv[0]=1;
    for(int i=1;iint x=i;
        for(int j=1;j<=m;j++){
            c[i][j]=c[i-1][j];
            while(!(x%a[j]))x/=a[j],c[i][j]++;
        }
        fac[i]=1ll*fac[i-1]*x%p;
        inv[i]=Pow(fac[i],phi-1);
    }
}
inline int Calc(int x,int y){
    if(x<y)return 0;
    if(!y)return 1;
    int Ans=1ll*fac[x]*inv[y]%p*inv[x-y]%p;
    for(int i=1;i<=m;i++)Ans=1ll*Ans*Pow(a[i],c[x][i]-c[y][i]-c[x-y][i])%p;
    return Ans;
}
int main(){
    scanf("%d%d%d%d",&n,&p,&l,&r);
    Init();
    for(i=0;i<=n;i++)len=n-i,Ans=(Ans+1ll*(Calc(len,l+len+1>>1)-Calc(len,(min(r,len)+len>>1)+1))*Calc(n,i))%p;
    if(Ans<0)Ans+=p;
    cout<return 0;
}

你可能感兴趣的:(数论)