CodeForces 896D Nephren Runs a Cinema(组合计数+数论+数形结合)

来源:互联网 发布:装修公司哪家好 知乎 编辑:程序博客网 时间:2024/06/05 10:30

D. Nephren Runs a Cinema
time limit per test:2.5 seconds
memory limit per test:256 megabytes
input:standard input
output:standard output

Lakhesh loves to make movies, so Nephren helps her run a cinema. We may call it No. 68 Cinema.

However, one day, the No. 68 Cinema runs out of changes (they don't have 50-yuan notes currently), but Nephren still wants to start their business. (Assume thatyuan is a kind of currency in Regulu Ere.)

There are three types of customers: some of them bring exactly a 50-yuan note; some of them bring a 100-yuan note and Nephren needs to give a 50-yuan note back to him/her; some of them bring VIP cards so that they don't need to pay for the ticket.

Now n customers are waiting outside in queue. Nephren wants to know how many possible queues are there that they are able to run smoothly (i.e. every customer can receive his/her change), and that the number of 50-yuan notes they have after selling tickets to all these customers is between l and r, inclusive. Two queues are considered different if there exists a customer whose type is different in two queues. As the number can be large, please output the answer modulop.

Input

One line containing four integers n (1 ≤ n ≤ 105),p (1 ≤ p ≤ 2·109),l and r (0 ≤ l ≤ r ≤ n).

Output

One line indicating the answer modulo p.

Examples
Input
4 97 2 3
Output
13
Input
4 100 0 4
Output
35
Note

We use A, B and C to indicate customers with 50-yuan notes, customers with 100-yuan notes and customers with VIP cards respectively.

For the first sample, the different possible queues that there are 2 50-yuan notes left are AAAB, AABA, ABAA, AACC, ACAC, ACCA, CAAC, CACA and CCAA, and the different possible queues that there are3 50-yuan notes left are AAAC, AACA, ACAA and CAAA. So there are13 different queues satisfying the first sample. Similarly, there are35 different queues satisfying the second sample.



        非常巧妙的一道综合数学题……

        大致题意:电影院有三种方式买票,要么花五十元买票进去,要么用卡不用钱,要么给一百元然后找五十元。然后一开始卖票的人没有钱找,然后问你总共有多少种方案,可以使得卖票的人不会出现没钱可找,且最后剩余的50元钱的张数在区间[l,r]之间。

        首先,我们考虑简化版的问题,先不考虑用卡消费的方式,而且最后剩余的五十元钱张数为0。在这种情况下,依稀记得哪个学长讲过,对应可以转化到坐标轴上,人初始时在原点。然后五十元的相当于向前向上走一个单位,然后一百元对应向前向下走一个单位,要求最后从原点(0,0)走到(n,0)。那么显然在不考虑没钱找的情况下,方案数为C(n,n/2),即向上走和向下走的次数要相同。接下来再考虑这个不合法的情况,相当于走的过程中不能低于x轴。这里我们可以考虑对称性,对于一个不合法方案,我们能找到第一个不合法点,设为(a,-1),如果我们以y=-1为对称轴,把a之前的所有路线对称一边,那么显然路线变成了一条从(0,-2)到(n,0)的路线。具体例子可以见下图。仔细分析后发现,任何一条从(0,-2)到(n,0)的路线都能够对应一条不合法的方案,所以这个从(0,-2)到(n,0)的路线条数即为不合法方案数。因此,方案数为C(n,n/2)-C(n,n/2-1)

        然后,现在要求最后的五十元钱的张数要在区间[l,r]之间,那么无非就是改变一下最后的终点而已。可以得到最后的表达式:C(n,(n-l-1)/2)-C(n,(n-min(r,n))/2)也可以写成C(n,(n+l+1)/2)-C(n,(n+min(r,n))/2)。最后加上刷卡的情况,这个更好说,相当于n次移动中,有那么几次没有动,于是我们可以枚举有i次用卡,即i次没有动,对应选取这i个位置的方案数有C(n,i)种,那么相当于就是可以走剩下的n-i步达到之前的条件。总的来说就是ΣC(n,i)*(C(n-i,(n-i+l+1)/2)-C(n-i,(n-i+min(r,n-i))/2))。

        如果你觉得到了这里,这题就差不多了,那可就差远了……好戏还在后头……这里的取模是对任意数字取模!也就意味着我们无法简单的求取逆元。

        我们知道,组合数的求法很依赖于对逆元的求取,但是这里模数不是质数,简单的方法行不通,对于一些与模数不互质的数字,它们可能在剩余系下没有逆元。具体方法的话我也是理解了好久,今天才看到了一个说的稍微详细点的。我们平时考虑模数求逆元的时候,最低的条件是根据欧拉定理互质即可。那么我们这里求逆元的时候,可以认为的转换一下,使得两个数字互质。具体做法的话就是,求模数和所需求逆元的数字的GCD,然后数字除以GCD之后就会与模数互质。这样用它的时候我们相当于少乘了GCD,再人为地记录这个GCD,到时候乘回去即可。本题涉及到的逆元是阶乘逆元,而且数字较多,所以采用分解质因数找公共质因子的方式快速求GCD,并记录质因子的指数(相当于对GCD进行了分解质因数)。具体见代码:

#include<bits/stdc++.h>#define LL long long#define N 100010using namespace std;LL inv[N],fac[N],p[N],t[N][20];int n,l,r,P,tot,Phi;int phi(int k)//欧拉定理需要求P的phi{    int i,s;    s = k;    for(i = 2;i * i <= k; i++)    {        if(k % i == 0) s = s / i * (i - 1);        while(k % i == 0) k /= i;    }    if(k > 1) s = s / k * (k - 1);    return s;}LL qpow(LL a,LL b,LL mod)//qpow{    LL res=1;    while(b)    {        if (b&1) res=res*a%mod;        b>>=1; a=a*a%mod;    }    return res;}void init(){    LL x=P;    Phi=phi(P);    for(int i=2;(LL)i*i<=x;i++)//对P进行质因数分解    {        if (x%i) continue;        while(x%i==0) x/=i;        p[++tot]=i;    }    if (x>1) p[++tot]=x;    inv[0]=inv[1]=1;    fac[0]=fac[1]=1;    for(int i=2;i<N;i++)    {        x=i;        for(int j=1;j<=tot;j++)        {            t[i][j]=t[i-1][j];//由于是阶乘,所以要继承上一个数字的信息            if (x%p[j]) continue;            while(x%p[j]==0) x/=p[j],t[i][j]++;//寻找公共质因子,并记录指数        }        fac[i]=fac[i-1]*x%P;//对剩下的东西求阶乘、逆元        inv[i]=qpow(fac[i],Phi-1,P);//欧拉定理求逆元    }}LL C(int n,int m){    LL res=0;    if (n<m) return 0;    if (m==0) return 1;    res=fac[n]*inv[m]%P*inv[n-m]%P;    for(int i=1;i<=tot;i++)        res=res*qpow(p[i],t[n][i]-t[m][i]-t[n-m][i],P)%P;//求组合数的时候,把少乘的公共质因子乘回去    return res;}int main(){    scanf("%d%d%d%d",&n,&P,&l,&r);    init(); LL ans=0;    for(int i=0;i<=n;i++)    {        int m=n-i;        ans=(ans+C(n,i)*(-C(m,(m+min(r,m)>>1)+1)+C(m,m+l+1>>1))%P)%P;    }    printf("%I64d\n",ans);    return 0;}

原创粉丝点击