数论
gcd
这个就很简答了,直接看代码
int gcd(int x,int y){
return !y?x:gcd(y,x%y);
}
gcd也有一点点的性质
主要还是 结合律 比较有用
还有就是这个算法的复杂度合适最大呢,其实就是去求 斐波那契数列 的相邻两项的gcd,这也是一个比较神奇的性质
太简单了,所以来一个难一点的
exgcd
这个可以干啥呢
求 ax+by=n.
之后就是代码
void exgcd(int a,int b,int &x,int &y){
if(!b) {
x=1,y=0;
return 0;
}
exgcd(b,a%b,x,y);
int t=x;
x=y;
y=t-a/b*y;
}
当然这个也可以求 ax≡b(modn) 的解
只用求 ax+ny=b 的一组特殊解,之后转化为最小正解就好了
这个只适用于b可以整除gcd(a,n)的情况
这个也给我们提供了线性同余方程的转化方法
其实主要还是要去理解题目的转化引导
费马小定理
哪这个有啥用。
首先可以判断一个数是不是质数,只用随便去找一个a,之后看a的p-1次方mod p是不是等于1
还有啥用,就是求乘法逆元
这里的x就是我们要找的乘法逆元,最后只用快速幂加速一下就OK了,
但是只适用于p的质数的情况
int n,q;
ll fast(int b,int p){
ll ans=1;
b%=q;
while(p){
if(p%2==1) {
ans*=b;
ans%=q;
}
b=b*b%q;
p/=2;
}
return ans;
}
int main(){
scanf("%d%d",&n,&q);
printf("1\n");
for(int i=2;i<=n;i++){
//cout<<i<<" ";
printf("%lld\n",fast(i,q-2));
}
return 0;
}
中国剩余定理
这个就是韩信点兵的原理
这个是用来求解什么的呢
这个的证明是什么呢,其实也不是很难、
线性筛素数
这个其实就是** 希望每一个和数都被他的最小质因数给筛掉** 所以就可以达到线性的程度
至于原理其实我不是很懂,但是用处也不是很大所以就去理解一下这个代码
bool yuan[maxn];//判断是不是素数
int su[maxn];//存每一个素数
int cnt=0;
void shai(int x){
memset(yuan,1,sizeof(yuan));
yuan[1]=0;
for(int i=2;i<=n;i++){
if(yuan[i])su[++cnt]=i;
for(int j=1;j<=cnt&&i*su[j]<=n;j++){
yuan[su[j]*i]=0;
if(i*su[j]==0) break;//核心代码
}
}
return;
}
裴蜀定理
这个是啥呢,其实就是说
卢卡斯定理
这个是用来快速求解组合数取模的值的
主要的实现是需要乘法逆元,组合数计算的辅助
具体内容看图
代码实现
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll p,t,n,m;
long long fast(ll b,ll pp){
long long ans=1;
while(pp){
if(pp%2==1) ans=ans*b%p;
b=b*b%p;
pp/=2;
}
return ans;
}
long long C(ll n,ll m){//n个数取m个的组合数,即c n,m
ll son=1,mum=1;//分子和分母
if(n<m) return 0;
if(m>n-m) m=n-m;
for(int i=0;i<m;i++){
son=(son*(n-i))%p;
mum=(mum*(i+1))%p;
}// son从n乘到(n-m+1)mum从1乘到m
// cout<<mum<<" "<<son<<endl;
return son*fast(mum,p-2)%p;
}
ll lucas(ll n,ll m){//n个数中取出m个
if(m==0) return 1;
return lucas(n/p,m/p)*C(n%p,m%p)%p;
}
int main(){
scanf("%lld",&t);
while(t--){
scanf("%lld%lld%lld",&n,&m,&p);
printf("%lld\n",lucas(n+m,m));
}
return 0;
}