codeforce 896D

一道比较良心的数论题:

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

C(n,x)=∑(i=L to R)C(nx,i)−C(nx,i+1)。
证明如下:我们可以先取X个VIP客人出来,因为其可以插入任意的位置。
那么我们只要求合法的50,100元客户序列。
我们先看一下卡特兰数的推导:
------------------------------------------------------分割线--------------------------------------------------------------------------

 事实上,可以认为问题是,任意两种操作,要求每种操作的总次数一样,且进行第k次操作2前必须先进行至少k次操作1。我们假设一个人在原点,操作1是此人沿右上角45°走一个单位(一个单位设为根号2,这样他第一次进行操作1就刚好走到(1,1)点),操作2是此人沿右下角45°走一个单位。第k次操作2前必须先进行至少k次操作1,就是说明所走出来的折线不能跨越x轴走到y=-1这条线上!在进行n次操作1和n此操作2后,此人必将到到达(2n,0)!若无跨越x轴的限制,折线的种数将为C(2n,n),即在2n次操作中选出n次作为操作1的方法数。

 

现在只要减去跨越了x轴的情况数。对于任意跨越x轴的情况,必有将与y=-1相交。找出第一个与y=-1相交的点k,将k点以右的折线根据y=-1对称(即操作1与操作2互换了)。可以发现终点最终都会从(2n,0)对称到(2n,-2)。由于对称总是能进行的,且是可逆的。我们可以得出所有跨越了x轴的折线总数是与从(0,0)到(2n,-2)的折线总数。而后者的操作2比操作1要多0-(-2)=2次。即操作1为n-1,操作2为n+1。总数为C(2n,n-1)。

 
------------------------------------------------------分割线----------------------------------------------------------------------
我们同样推导,50元视为向上走,100元视为向下走,那么我们设50元X,100元Y,那么我们的目标点为(X+Y,X-Y),有C(2X,X-Y)种走法。
我们作对称点(X+Y,-2-X+Y),有C(2Y-2,X-Y-2)种方法,我们将其化简,可以得到C(X+Y,X)-C(X+Y,X+1)。
那么公式便简化成:
    ans=∑C(N,i)*(C(N-i,L)-C(N-i,R+1)),我们考虑如和处理模数。
我们在统计阶乘时,把与模数的GCD提出来,最后计算时乘(除)回去。
就酱紫。
#include<bits/stdc++.h>
#define sight(c) ('0'<=c&&c<='9')
#define LL long long
#define int LL 
#define N 100017
#define dg  
inline void read(int &x){
    static char c;
    for (c=getchar();!sight(c);c=getchar());
    for (x=0;sight(c);c=getchar())x=x*10+c-48;
}
using namespace std;
void write(int x){if (x<10) {putchar('0'+x); return;} write(x/10); putchar('0'+x%10);}
inline void writel(int x){ if (x<0) putchar('-'),x*=-1; write(x); putchar('\n'); }
LL qsm(LL x,LL y,LL mo) {
    static LL anw;
    for (anw=1;y;y>>=1,x=x*x%mo)  if (y&1) anw=anw*x%mo; 
    return anw;
}
int a[N>>10],tot,p,l,r,n,x,c[N][N>>10];
LL t,mo,f[N],ni[N],AA1,AA2,AA3,ANW,ans;
void Int() {
    t=mo=p; 
    for (int i=2;i*i<=p;i++)  {
        if (p%i==0) a[++tot]=i;
        while (p%i==0) p/=i;
    }  if (p^1) a[++tot]=p;
    for (int i=1;i<=tot;i++) t=t/a[i]*(a[i]-1);
    f[0]=ni[0]=1;
    for (int i=1;i<=n+7;i++) {
        x=i; for (int j=1;j<=tot;j++) {
         c[i][j]=c[i-1][j];
         while (x%a[j]==0) c[i][j]++,x/=a[j];}
        f[i]=f[i-1]*x%mo; 
        ni[i]=qsm(f[i],t-1,mo);
    } 
}
LL C(int x,int y) {
    if (x<y) return 0;
    if (!y) return 1;
    static LL anw; anw=f[x]*ni[y]%mo*ni[x-y]%mo;
    for (int i=1;i<=tot;i++) anw=anw*qsm(a[i],c[x][i]-c[y][i]-c[x-y][i],mo)%mo;
    return anw; 
}
LL ask(int siz){
    AA1=C(n,siz); 
    AA2=C(siz,siz+l+1>>1);
    AA3=C(siz,(min(siz,r)+siz>>1)+1);
    ANW=(AA2-AA3)%mo; if (ANW<0) ANW+=mo;
//    dg("%d %d %d\n",AA2,AA3,AA1);
    return ANW*AA1%mo;
}
signed main () {
//    freopen("d.in","r",stdin);
    read(n); read(p); read(l); read(r);
    Int();
    for (int i=n;~i;i--) 
      (ans+=ask(n-i))%=mo;
    writel(ans%mo>=0?ans%mo:ans%mo+mo);
    return 0;
}

 

 
 
posted @ 2018-01-19 21:28  泪寒之雪  阅读(322)  评论(0编辑  收藏  举报