理论待更新,今天更完
Lacus定理
注意!如果m%p>n%p,那么C(n%p,m%p,p)=0 也就是下标小于上标 结果也就是0了 这一点务必要注意
#include<cstdio>//求单个组合数 卢卡斯定理 n<=1e18 m<=1e18 p为素数但比较小! p<=1e4 注意!如果m%p>n%p,那么C(n%p,m%p,p)=0 也就是下标小于上标 结果也就是0了 这一点务必要注意
#include<string.h>//易错:计算时记得换long long否则溢出会得到负数!
#include<vector>
using namespace std;
typedef long long ll;
const int p=1e5+3;//注意,1e5+7不是质数,他是97*1031的结果!
inline ll read(){//快读 若在与同一行有string的情况下使用,把下面的readS也写上 如果变long long,记得把s前的int也变了
char ch=getchar();
ll s=0,w=1;//long long s=0,w=1;
while(ch<'0' || ch>'9'){
if(ch=='-') w*=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){//不要写成|| !
s=ch-'0'+(s<<3)+(s<<1);//最大易错点!s=而不是s+=
ch=getchar();//不要忘记!
}//如果这个数字后面有回车,这个循环会接收回车,所以不需要在额外来个getchar()
return s*w;
}
ll exgcd(ll a,ll b,ll &x,ll &y){//扩展欧几里得算法 ax+by=c 求解a,b
if(!b){
x=1,y=0;
return a;
}
ll ans=exgcd(b,a%b,x,y);
ll temp=x;
x=y;
y=temp-a/b*y;
return ans;
}
ll inv(ll a,ll p){//求逆元 ax1(mod p)=>a*x+p*y=1 这里我们求x 要求a与p互质,即gcd(a,p)=1 如果不存在返回-1
ll x,y,gcd;
gcd=exgcd(a,p,x,y);
return (x%p+p)%p; //x%p+p确保逆元为正数
}
ll C(ll n, ll m) {//求组合数
ll ans=1,numP=0;//ans存放计算结果,numP统计分子中的p比分母中的p多几个
for(ll i = 1; i <= m; i++) {//对分子每个数进行质因子分解
ll temp= n - m + i;//分子
while(temp%p==0) {
numP++;
temp/=p;
}
ans=ans*temp%p;//乘以分子中除了p以外的部分
temp=i;//分母
while(temp%p==0) {
numP--;
temp/=p;
}
ans=ans*inv(temp,p)%p;//除以分母中除了p以外的部分
}
if(numP>0) return 0; //分子中p的个数多于分母,直接返回0
else return ans;//分子中p的个数等于分母,返回计算的结果
}
ll Lucas(ll n,ll m){
if(m==0) return 1;
if(m%p > n%p) return 0;//注意!如果m%p>n%p,那么C(n%p,m%p,p)=0 也就是下标小于上标 结果也就是0了 这一点务必要注意
return C(n%p, m%p)*Lucas(n/p,m/p)%p;
}
int main() {
ll t=Lucas(1e17+48,1e8+6);//18716 19718 5393
printf("%lld\n",t);
return 0;
}
模板题https://www.luogu.com.cn/problem/P3807
杨辉三角完全打表
#include<cstdio>//杨辉三角全打表,1e4范围较快,受栈空间大小和时间影响
int res [10050] [10050] = {};
const int N=10000,p=1e9+7;
int C(int n, int m) {
if(m == 0 || m == n) return 1; //C(n, 0) = C(n, n) = 1
if (res [n] [m] != 0) return res [n] [m]; //已经有值
return res[n] [m] = (C(n - 1, m) + C(n - 1, m - 1))%p; //赋值并返回
}
void calC() {
for (int i = 0; i <= N; i++) {
res [i] [0] = res [i] [i] = 1; //初始化边界
}
for (int i = 2; i <= N; i++) {//i为下标j为上标
for(int j = 0; j <= i / 2; j++) {
res[i] [j] = (res[i - 1] [j] + res[i-1] [j-1])%p; //递推计算C(i, j)
res[i] [i -j] = res[i] [j]; //C(i, i - j) = C(i, j)
}
}
}
int main(){
calC();
return 0;
}
杨辉三角不完全打表
#include<cstdio>//利用C(n,m)=C(n,m-1)+C(n-1,m-1)递推计算 适用于n,m<=1e4的情况 对p的大小和素性没有限制 (栈空间数组大小限制和时间限制)
const int p=1e9+7;
int res [10010] [10010] = {};
int C(int n, int m) {//递归
if(m == 0 || m == n) return 1; //C(n, 0) = C(n, n) = 1
if (res [n] [m] != 0) return res [n] [m]; //已经有值
return res[n] [m] = (C(n - 1, m) + C(n - 1, m - 1))%p; //赋值并返回
}
int main(){
int t=C(9010,3329);
printf("%d",t);
}
质因子分解法
#include<string.h>//求单个C(n,m)质因子分解法 时间复杂度klogn k为不超过n的质数个数 n,m<=1e6 对p的大小和素性无限制
#include<cstdio>//易错:计算时记得换long long否则溢出会得到负数!
#include<vector>
using namespace std;
typedef long long ll;
const int N=1e6+10,p=1e9+7; //N为素数表大小
bool isnp[N]; //isnp[i]=0则i为素数,否则为合数
vector<int> prime; // 质数表
void init() {
for (int i = 2; i < N; i++) {
if (!isnp[i])
prime.push_back(i);
for (int p=0; p<prime.size(); p++) {
if (i*prime[p] >= N) break;
isnp[i*prime[p]]= 1;
if (i % prime[p] == 0) break;
}
}
}
int cal(int n, int p) {//求n!的质因子p的指数
int ans = 0;
while (n) {
ans+=n/p;
n/=p;
}
return ans;
}
ll binaryPow(ll a, ll b, ll m) {//快速幂
if (b == 0) return 1;
if(b % 2 == 1) return a* binaryPow(a, b - 1, m) % m;
else {
ll mul = binaryPow(a, b / 2, m);
return mul* mul % m;
}
}
ll C(ll n, ll m) {//计算C(n,m)%p
ll ans = 1;
for(ll i = 0; prime[i] <= n; i++) {
ll c = cal(n, prime[i]) - cal(m, prime[i]) - cal(n - m, prime[i]);
ans= ans* binaryPow(prime[i], c, p) % p;
}
return ans;
}
int main() {
init();
ll t=C(1e5+4513,1e5+7);
printf("%lld",t);
return 0;
}
质因子分解法2
#include<cstdio>//求单个组合数 定义式变形法 n<=1e9 m<=1e6 p为素数 p可以小于m
#include<string.h>//易错:计算时记得换long long否则溢出会得到负数!
#include<vector>
using namespace std;
typedef long long ll;
const int p=1e9+7;
ll exgcd(ll a,ll b,ll &x,ll &y){//扩展欧几里得算法 ax+by=c 求解a,b
if(!b){
x=1,y=0;
return a;
}
ll ans=exgcd(b,a%b,x,y);
ll temp=x;
x=y;
y=temp-a/b*y;
return ans;
}
ll inv(ll a,ll p){//求逆元 ax1(mod p)=>a*x+p*y=1 这里我们求x 要求a与p互质,即gcd(a,p)=1 如果不存在返回-1
ll x,y,gcd;
gcd=exgcd(a,p,x,y);
return (x%p+p)%p; //x%p+p确保逆元为正数
}
ll C(ll n, ll m) {//求组合数
ll ans=1,numP=0;//ans存放计算结果,numP统计分子中的p比分母中的p多几个
for(ll i = 1; i <= m; i++) {//对分子每个数进行质因子分解
ll temp= n - m + i;//分子
while(temp%p==0) {
numP++;
temp/=p;
}
ans=ans*temp%p;//乘以分子中除了p以外的部分
temp=i;//分母
while(temp%p==0) {
numP--;
temp/=p;
}
ans=ans*inv(temp,p)%p;//除以分母中除了p以外的部分
}
if(numP>0) return 0; //分子中p的个数多于分母,直接返回0
else return ans;//分子中p的个数等于分母,返回计算的结果
}
int main() {
ll t=C(196282623,1002422);
printf("%lld\n",t);
return 0;
}