[ 数论 ] Codeforces896D

来源:互联网 发布:php常用面试题 编辑:程序博客网 时间:2024/06/15 13:51

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

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


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

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

#include<bits/stdc++.h>using namespace std;#define N 100010#define M 33int 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;i<N;i++){        int 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<<Ans<<endl;    return 0;}
原创粉丝点击