标签:取模 HDU 格子 int long 中间 瞬间 5698 mod
题面:
瞬间移动
Input file: standard input Output file: standard output Time limit: 2 second Memory limit: 256 megabytes 有一个无限大的矩形,初始时你在左上角(即第一行第一列),每次你都可以选择一个右下方格子,并瞬移过去(如从下图中的红色格子能直接瞬移到蓝色格子),求到第n行第m列的格子有几种方案,答案对1000000007取模。 Input 多组测试数据。两个整数n,m(2≤n,m≤100000) Output 一个整数表示答案 Example Input 4 5 Output 10
题目描述:
无题目分析:
这题用到的知识点:快速幂,组合数,逆元与费马小定理。我们先分析一下样例:从格子(1, 1)到格子(4, 5): 从这里我们可以看到,其中一种方案就是:直接到达终点。其次,我们想到的方案是通过中间一个格子来到达终点: 最后,我们可以选取中间两个格子来到达终点: 一共10种情况,这里我们是以中间选多少格子来分类讨论的:中间不选格子:1种 中间选1个格子:6种 中间选2个格子:3种那么,我们怎样计算出中间选x个格子有多少种情况? 我们先看中间选1个格子是怎样选出来的: 当我们确定了1行和1列的时候,就可以确定1个格子的位置。按照组合数学的知识,我们可以知道中间选一个格子的方案数为:C(2, 1)*C(3, 1),也就是6。 当我们中间选2个格子的时候: 这时,我们确定了2行2列,因为每一次我们只能选右下方的格子,所以我们选择右下方对角线方向的直线的交点,这时2个格子的位置就被确定了。 方案数:C(2, 2)*C(3, 2),也就是3。 所以,到达格子(4, 5)的方案数就是:C(4-2, 0)*C(5-2, 0) + C(4-2, 1)*C(5-2, 1) + C(4-2, 2)*C(5-2, 2)。 如果是到达格子(5, 4),其实方案数也是一样的(对称关系),只要看成(4, 5)的情况就好了。 有的人可能有疑问:中间如果要选3个格子或以上怎么办?其实道理和上面一样,我就不多说直接上图吧: 所以计算公式是:C(n-2, 0)*(m-2, 0) + C(n-2, 1)*(m-2, 1) + ...... + C(n-2, n-2)*C(m-2, n-2) (n <= m 时)
如果 n > m,直接交换 n,m 就行了。 这时,我们计算组合数C(a, b) = a! / ( b! * (a-b)! )。因为组合数太大,所以答案要求我们对组合数进行取模,也就是C(a, b) % p (p == 1000000007)。但是取模就涉及到一个问题:我们可以对含有乘法的运算进行边乘边取模,但是不能对含有除法的运算进行取模运算。举个例子:对于乘法来说,(6*3)%4 == (6%4)*3%4;但是对于除法来说,(6/3)%4 != (6%4) / (3%4) %4 (左侧结果是2,右侧结果是0)。为了把 / ( b! * (a-b)! ) 等效为一个乘法的运算,也就是除于一个数 x 取模的结果,等于乘于一个数 y 取模的结果,我们就要用到逆元的知识。逆元的作用就是:如果数 w,x,y,p符合这样的运算:w / x % p == w * y % p,那么y就是x的逆元。如果p是质数,那么根据费马小定理(这里就不推导了( ̄▽ ̄)"),这个 y == x p-2 。而这个 x p-2 怎么算?如果直接求速度太慢了,这时就要用到快速幂,把O(p-2)的时间复杂度降到O( log(p-2) )。最后,我们预处理一下数据就行了。记得要边乘边模才不会溢出(做完一次乘法就立刻取模,注:取模运算符的优先级和乘除法是一样的) AC代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 const long long mod = 1000000007; 7 long long f[100005]; 8 long long inv[100005]; 9 10 long long quick_pow(long long base, long long x){ //快速幂 11 long long ans = 1; 12 while(x){ 13 if(x & 1) ans = ans*base%mod; 14 base = base*base%mod; 15 x >>= 1; 16 } 17 return ans%mod; 18 } 19 20 long long comb(int a, int b){ //组合数 21 return f[b]*inv[a]%mod*inv[b-a]%mod; 22 } 23 24 void fac(){ //预处理 25 f[0] = 1; 26 inv[0] = 1; 27 for(int i = 1; i <= 100005; i++){ 28 f[i] = f[i-1]*i%mod; //得出阶乘 29 } 30 for(int i = 100005; i > 0; i--){ 31 inv[i] = quick_pow(f[i], mod-2); //得出阶乘对应的逆元 32 } 33 } 34 35 int main(){ 36 int n, m; 37 fac(); 38 while(~scanf("%d%d", &n,&m)){ 39 if(n > m) swap(n, m); //交换n, m, 使小的在前,大的在后 40 n = n-2; 41 m = m-2; 42 long long ans = 0; 43 for(int i = 0; i <= n; i++){ 44 ans = (ans + comb(i, n)%mod*comb(i, m)%mod)%mod; //推导出的公式 45 } 46 cout << ans << endl; 47 } 48 return 0; 49 }
标签:取模,HDU,格子,int,long,中间,瞬间,5698,mod 来源: https://www.cnblogs.com/happy-MEdge/p/10498375.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。