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

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.

CodeForces 896D Nephren Runs a Cinema(组合计数+数论+数形结合)_第1张图片

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)

CodeForces 896D Nephren Runs a Cinema(组合计数+数论+数形结合)_第2张图片

        然后,现在要求最后的五十元钱的张数要在区间[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
#define LL long long
#define N 100010
using 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>1)+1)+C(m,m+l+1>>1))%P)%P;
    }

    printf("%I64d\n",ans);
    return 0;
}

你可能感兴趣的:(---------Online,Judge--------,CodeForces,组合计数,数论,数形结合)