组合数公式C(n,m)=n*(n-1)……(n-m+1)/m!
885.求组合数 I
链接:https://www.acwing.com/problem/content/887/
给定 n 组询问,每组询问给定两个整数 a,b,请你输出 C(a,b)mod(10^9+7) 的值。
f[i][j]=f[i-1][j-1]+f[i-1][j]
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
const int N=2005;
int f[N][N],a,b,n;
int main(){
for(int i=0;i<=2000;i++){
for(int j=0;j<=i;j++){
if(!j)f[i][j]=1;
else f[i][j]=(f[i-1][j]+f[i-1][j-1])%mod;
}
}
cin>>n;
for(int i=1;i<=n;i++){
cin>>a>>b;
cout<<f[a][b]<<endl;
}
return 0;
}
886.求组合数 II
链接:https://www.acwing.com/problem/content/888/
给定 n 组询问,每组询问给定两个整数 a,b,请你输出 C(a,b)mod(109+7) 的值。n<=10000,a,b<=1e5
逆元求解,C(a,b)=a!/b!/(a-b)!mod 1e9+7,
//https://www.acwing.com/problem/content/888/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100005;
const int mod=1e9+7;
int n,a,b;
int fact[N],infact[N];
int qmi(int a,int k,int p){
int res=1;
while(k){
if(k&1)res=(LL)res*a%p;
k>>=1;
a=(LL)a*a%p;
}
return res;
}
int main(){
cin>>n;
fact[0]=infact[0]=1;
for(int i=1;i<N;i++){
fact[i]=(LL)fact[i-1]*i%mod;
infact[i]=(LL)infact[i-1]*qmi(i,mod-2,mod)%mod;
}
while(n--){
cin>>a>>b;
cout<<(LL)fact[a]*infact[a-b]%mod*infact[b]%mod<<endl;
}
return 0;
}
887.求组合数 III
链接:https://www.acwing.com/problem/content/889/
给定 n 组询问,每组询问给定三个整数 a,b,p,其中 p 是质数,请你输出 C(a,b)modp 的值。
1≤n≤20,1≤b≤a≤1e18,1≤p≤1e5。
lucas定理,C(a,b)%p,p为质数,C(a,b)%p=C(a%p,b%p)*C(a/p,b/p)%p。
//https://www.acwing.com/problem/content/889/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n,p;
LL a,b;
int qmi(int a,int k,int p){
int res=1;
while(k){
if(k&1)res=(LL)res*a%p;
a=(LL)a*a%p;
k>>=1;
}
return res;
}
int C(int a,int b,int p){
if(b>a)return 0;
int res=1;
for(int i=1,j=a;i<=b;i++,j--){
res=(LL)res*j%p;
res=(LL)res*qmi(i,p-2,p)%p;
}
return res;
}
int lucas(LL a,LL b,int p){
if(a<p&&b<p)return C(a,b,p);
else return (LL)C(a%p,b%p,p)*lucas(a/p,b/p,p)%p;
}
int main(){
cin>>n;
while(n--){
cin>>a>>b>>p;
cout<<lucas(a,b,p)<<endl;
}
return 0;
}
888.求组合数 IV
链接:https://www.acwing.com/problem/content/890/
输入 a,b,求 C(a,b) 的值。
注意结果可能很大,需要使用高精度计算,a,b<=5000
用线性筛求出所有的质数,之后统计每个质数出现的次数即可,然后高精度统计即可
//https://www.acwing.com/problem/content/890/
#include<bits/stdc++.h>
using namespace std;
const int N=5005;
bool flag[N];
int primes[N],cnt,sum[N],a,b;
void get_primes(int x){
for(int i=2;i<=x;i++){
if(flag[i]==0)primes[++cnt]=i;
for(int j=1;primes[j]<=x/i;j++){
flag[primes[j]*i]=1;
if(i%primes[j]==0)break;
}
}
}
int get(int x,int p){
int res=0;
while(x){
res+=x/p;
x/=p;
}
return res;
}
vector<int> mul(vector<int>a,int b){
vector<int>c;
int t=0;
for(int i=0;i<a.size();i++){
t=t+a[i]*b;
c.push_back(t%10);
t/=10;
}
while(t){
c.push_back(t%10);
t/=10;
}
return c;
}
int main(){
cin>>a>>b;
get_primes(a);
for(int i=1;i<=cnt;i++){
int p=primes[i];
sum[i]=get(a,p)-get(a-b,p)-get(b,p);
}
vector<int>res;
res.push_back(1);
for(int i=1;i<=cnt;i++){
for(int j=1;j<=sum[i];j++){
res=mul(res,primes[i]);
}
}
for(int i=res.size()-1;i>=0;i--)cout<<res[i];
cout<<endl;
return 0;
}
889.满足条件的01序列
链接:https://www.acwing.com/problem/content/891/
给定 n 个 0 和 n 个 1,它们将按照某种顺序排成长度为 2n 的序列,求它们能排列成的所有序列中,能够满足任意前缀序列中 0 的个数都不少于 1 的个数的序列有多少个。
输出的答案对 1e9+7 取模。
由图可知,任何一条不合法的路径(如黑色路径),都对应一条从 (0,0)(0,0) 走到 (n−1,n+1)(n−1,n+1) 的一条路径(如灰色路径)。而任何一条 (0,0)(0,0) 走到 (n−1,n+1)(n−1,n+1) 的路径,也对应了一条从 (0,0)(0,0) 走到 (n,n)(n,n) 的不合法路径。
答案如图,即卡特兰数。
//https://www.acwing.com/problem/content/891/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod=1e9+7;
int n;
int qmi(int a,int k,int p){
int res=1;
while(k){
if(k&1)res=(LL)res*a%p;
a=(LL)a*a%p;
k>>=1;
}
return res;
}
int main(){
cin>>n;
int a=n*2,b=n,res=1;
for(int i=a;i>a-b;i--)res=(LL)res*i%mod;
for(int i=1;i<=b;i++)res=(LL)res*qmi(i,mod-2,mod)%mod;
cout<<(LL)res*qmi(n+1,mod-2,mod)%mod<<endl;
return 0;
}
890.能被整除的数
链接:https://www.acwing.com/problem/content/892/
给定一个整数 n 和 m 个不同的质数 p1,p2,…,pm。
请你求出 1∼n 中能被 p1,p2,…,pm 中的至少一个数整除的整数有多少个。
容斥原理即可。
//https://www.acwing.com/problem/content/892/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n,m,mul,p[20],ans;
int main(){
cin>>n>>m;
for(int i=0;i<m;i++)cin>>p[i];
for(int i=1;i<(1<<m);i++){
int mul=1,cnt=0;
for(int j=0;j<m;j++){
if((i>>j)&1){
if((LL)mul*p[j]>n){
mul=-1;
break;
}
mul=mul*p[j];
cnt++;
}
}
if(mul!=-1){
if(cnt&1)ans+=n/mul;
else ans-=n/mul;
}
}
cout<<ans<<endl;
return 0;
}
876.快速幂求逆元
给定 n 组 ai,pi,其中 pi 是质数,求 ai 模 pi 的乘法逆元,若逆元不存在则输出 impossible。
注意:请返回在 0∼p−1 之间的逆元。
乘法逆元的定义
若整数 b,m 互质,并且对于任意的整数 a,如果满足 b|a,则存在一个整数 x,使得 a/b≡a×x(modm),则称 x 为 b 的模 m 乘法逆元,记为 b−1(modm)。
b 存在乘法逆元的充要条件是 b 与模数 m 互质。当模数 m 为质数时,bm−2 即为 b 的乘法逆元。
//https://www.acwing.com/problem/content/878/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n,a,p;
int qmi(int a,int k,int p){
int res=1;
while(k){
if(k&1)res=(LL)res*a%p;
a=(LL)a*a%p;
k>>=1;
}
return res;
}
int main(){
cin>>n;
while(n--){
cin>>a>>p;
if(a%p==0)cout<<"impossible"<<endl;
else cout<<qmi(a,p-2,p)<<endl;
}
return 0;
}
当n为质数时,可以用快速幂求逆元:
a / b ≡ a * x (mod n)
两边同乘b可得 a ≡ a * b * x (mod n)
即 1 ≡ b * x (mod n)
同 b * x ≡ 1 (mod n)
由费马小定理可知,当n为质数时
b ^ (n - 1) ≡ 1 (mod n)
拆一个b出来可得 b * b ^ (n - 2) ≡ 1 (mod n)
故当n为质数时,b的乘法逆元 x = b ^ (n - 2)
当n不是质数时,可以用扩展欧几里得算法求逆元:
a有逆元的充要条件是a与p互质,所以gcd(a, p) = 1
假设a的逆元为x,那么有a * x ≡ 1 (mod p)
等价:ax + py = 1
exgcd(a, p, x, y)
高斯消元
链接:https://www.acwing.com/problem/content/885/
#include<bits/stdc++.h>
using namespace std;
const int N=105;
const double eps=1e-8;
double a[N][N];
int n;
int gauss(){
int c,r;
for(r=0,c=0;c<n;c++){
int t=r;
for(int i=r+1;i<n;i++){
if(fabs(a[i][c])>fabs(a[t][c]))t=i;
}
if(fabs(a[t][c])<eps)continue;
for(int i=c;i<=n;i++)swap(a[r][i],a[t][i]);
for(int i=c+1;i<=n;i++)a[r][i]/=a[r][c];
for(int i=r+1;i<n;i++){
if(fabs(a[i][c])>eps){
for(int j=n;j>=c;j--){
a[i][j]-=a[i][c]*a[r][j];
}
}
}
r++;
}
if(r<n){
for(int i=r;i<n;i++){
if(fabs(a[i][n])>eps)return 2;
}
return 1;
}
for(int i=n-1;i>=0;i--){
for(int j=i+1;j<n;j++){
a[i][n]-=a[i][j]*a[j][n];
}
}
return 0;
}
int main(){
cin>>n;
for(int i=0;i<n;i++){
for(int j=0;j<=n;j++){
cin>>a[i][j];
}
}
int t=gauss();
if(t==2)cout<<"No solution"<<endl;
else if(t==1)cout<<"Infinite group solutions"<<endl;
else{
for(int i=0;i<n;i++){
if(fabs(a[i][n])<eps)a[i][n]=0;
printf("%.2lf\n",a[i][n]);
}
}
return 0;
}
欧拉函数
链接:https://www.acwing.com/problem/content/875/
给定 n 个正整数 ai,请你求出每个数的欧拉函数。
1∼N 中与 N 互质的数的个数被称为欧拉函数
1∼N 中与 N 互质的数的个数被称为欧拉函数,记为 ϕ(N)。
若在算数基本定理中,N=p1^a1*p2^a2…pm^am,则:
ϕ(N) = N×(p1−1)/p1×(p2−1)/p2×…×(pm−1)pm
筛法求欧拉函数
链接:https://www.acwing.com/solution/content/3952/
给定一个正整数 n,求 1∼n 中每个数的欧拉函数之和。n<=1e6
在用线性筛的时候,可以顺便求出欧拉函数。
//https://www.acwing.com/problem/content/876/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1000005;
int n,cnt;
bool flag[N];
int euler[N],primes[N];
void get_eulers(int n){
euler[1]=1;
for(int i=2;i<=n;i++){
if(flag[i]==0){
primes[++cnt]=i;
euler[i]=i-1;
}
for(int j=1;primes[j]<=n/i;j++){
int t=primes[j]*i;
flag[t]=1;
if(i%primes[j]==0){
euler[t]=euler[i]*primes[j];
break;
}
euler[t]=euler[i]*(primes[j]-1);
}
}
}
int main(){
cin>>n;
get_eulers(n);
LL ans=0;
for(int i=1;i<=n;i++)ans+=euler[i];
cout<<ans<<endl;
}
扩展欧几里得算法
链接:https://www.acwing.com/problem/content/879/
#include<bits/stdc++.h>
using namespace std;
int a,b,x,y,n;
int exgcd(int a,int b,int &x,int &y){
if(b==0){
x=1;y=0;
return a;
}
int g=exgcd(b,a%b,y,x);
y-=a/b*x;
return g;
}
int main(){
cin>>n;
while(n--){
cin>>a>>b;
exgcd(a,b,x,y);
cout<<x<<" "<<y<<endl;
}
return 0;
}
线性同余方程
链接:https://www.acwing.com/problem/content/880/
//https://www.acwing.com/problem/content/880/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n,a,b,m,x,y;
int exgcd(int a,int b,int &x,int &y){
if(!b){
x=1;y=0;
return a;
}
int g=exgcd(b,a%b,y,x);
y-=a/b*x;
return g;
}
int main(){
cin>>n;
while(n--){
cin>>a>>b>>m;
int g=exgcd(a,m,x,y);
if(b%g)cout<<"impossible"<<endl;
else cout<<(LL)b/g*x%m<<endl;
}
return 0;
}
表达整数的奇怪方式
参考:https://www.acwing.com/problem/content/description/206/
中国剩余定理模板
//https://www.acwing.com/problem/content/description/206/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int ans,n;
LL a1,m1,a2,m2,k1,k2;
LL exgcd(LL a,LL b,LL &x,LL &y){
if(b==0){
x=1;y=0;
return a;
}
LL g=exgcd(b,a%b,y,x);
y-=a/b*x;
return g;
}
int main(){
cin>>n;
cin>>a1>>m1;
for(int i=2;i<=n;i++){
cin>>a2>>m2;
LL g=exgcd(a1,a2,k1,k2);
if((m2-m1)%g){
ans=-1;
break;
}
k1=k1*(m2-m1)/g;
LL t=a2/g;
k1=(k1%t+t)%t;
m1=a1*k1+m1;
a1=a1/g*a2;
}
if(ans==-1)cout<<-1<<endl;
else cout<<(m1%a1+a1)%a1<<endl;
return 0;
}