组合数模板三:O(plog{2,n}),本题还要在此基础上乘最多20组测试数据
前置知识:组合数基本定义
解决核心:卢卡斯定理
时间复杂度:
问题:
所以对于本题来说,n<=1e18(组合数下界),则logn ≈ 60(以2为底),时间复杂度为60*1e5 = 6e6,但是由于有最多20组询问,因此最终算出来是1.2e8,虽然一秒内无法算出,但是可能题目设置的数据不是很强,所以能过。写法一是比视频中更为优化的写法。
一个要注意的点:
写法一:更优美的写法
其实优化之处就是在于把原本每次for循环都要qmi求的逆元优化到最后一步返回值的时候来求解(是对于C函数的改编)
#includeusing namespace std; #define int long long int qmi(int a,int k,int p) { int res = 1; while(k) { if(k&1) res = res*a%p; a = a*a%p; k>>=1; } return res; } int C(int a, int b, int p) { if(a < b) return 0; int down = 1, up = 1; for(int i=a, j=1; j<=b; i--, j++) { up = up * i % p; down = down * j % p; } return up * qmi(down, p-2, p) % p;//其实优化之处就是把原本每次for循环都要qmi求的逆元优化到最后一步返回值的时候来求解 } int lucas(int a,int b,int p)//lucas定理,套公式即可 { if(a > n; while(n--) { int a,b; int p; cin >> a >> b >> p; cout << lucas(a,b,p) << endl; } return 0; }
写法二:yls的写法
#includeusing namespace std; #define int long long int qmi(int a,int k,int p) { int res = 1; while(k) { if(k&1) res = res*a%p; a = a*a%p; k>>=1; } return res; } int C(int a,int b,int p)//从定义出发来编写的函数 { int res = 1; for(int i=1,j=a;i<=b;i++,j--) { res = res*j%p;//乘分子:a * (a-1) * .... * (a-b+1) res = res*qmi(i,p-2,p)%p;//除分母:原本是除上i,这里要转为乘上i的逆元,逆元结合费马小定理用快速幂来计算 } return res; } int lucas(int a,int b,int p)//lucas定理,套公式即可 { if(a > n; while(n--) { int a,b; int p; cin >> a >> b >> p; cout << lucas(a,b,p) << endl; } return 0; }



