CodeForces 896D Nephren Runs a Cinema(组合计数+数论+数形结合)
来源:互联网 发布:装修公司哪家好 知乎 编辑:程序博客网 时间:2024/06/05 10:30
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.
One line containing four integers n (1 ≤ n ≤ 105),p (1 ≤ p ≤ 2·109),l and r (0 ≤ l ≤ r ≤ n).
One line indicating the answer modulo p.
4 97 2 3
13
4 100 0 4
35
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;}
- CodeForces 896D Nephren Runs a Cinema(组合计数+数论+数形结合)
- Codeforces Round #449 (Div. 1) D. Nephren Runs a Cinema 卡特兰数,逆元,欧拉函数,
- CodeForces Round #223 div.1 D.Sereja and Cinema(组合计数)
- codeforces 380D. Sereja and Cinema(#223div1 组合数学)
- Codeforces 234D Cinema
- codeforces 896A Nephren gives a riddle dfs搜索
- CodeForces 349A Cinema Line (模拟)
- 【codeforces 897C】Nephren gives a riddle (递归)
- Codeforces 145D Lucky Pair (组合计数)
- CodeForces 396A 数论 组合数学
- CodeForces 349A Cinema Line
- 【数论】hdu5072 Coprime (容斥求互质+组合计数)
- codeforces 897C Nephren gives a riddle
- [codeforces] 897C Nephren gives a riddle
- Codeforces-830D Singer House(组合数+dp)
- CodeForces 240A A. Cinema 解题报告
- CodeForces Round #145(234D) - Cinema
- codeforces 82D Cinema Cashier dp 队列
- C语言小游戏—扫雷大作战
- 1077. 互评成绩计算 (20)
- 模板方法模式深度解析(二)
- easydss与h264编码算法由浅入深(一)
- spring bean parent属性详解
- CodeForces 896D Nephren Runs a Cinema(组合计数+数论+数形结合)
- pyCharm Process finished with exit code -1073740791 (0xC0000409)
- SQLite synopsis
- zabbix自定义item
- 多线程和多进程的区别(小结)
- bzoj4570 [Scoi2016]妖怪(凸包+对勾函数最值)
- eclipse的基本使用:
- 存储设备层次结构
- 如何理解JAVA中的封装