这个好久以前写的了,最近也没忙着专业课考试也没来得及弄这些,如果哪里有错请多包涵…
数论的东西主要还是靠理解,其实模板只是一个基础,真正遇到题肯定是需要变形的,这就需要对其有一个非常深刻的理解和认识了.所以主要还是理解原理
1.埃氏筛法
需要注意的是有些题没有必要打表 挨个判断即可 打表反而会超时
const int MAX_N=100000000;
bool vis[MAX_N+10];//vis数组表示该数是否为素数
void init(int n){//可不传值
//int m=sqrt(n+0.5);
int m=sqrt(MAX_N+0.5);//m的开方进一步缩小了计算范围
/*
原因我是这么理解的
一个数如果不是素数,那么对于这个打表来说
像26这种数字 它虽然是13的两倍,但是这个数早在i等于2的时候就已经被排除掉了
故最极端的情况就是169这种数,他是一个数的平方
唯一能判断出它不是素数的因子就是13即sqrt(n)
然后为了能够正好判断出这种情况+0.5将这种情况包含进去
至于证明的话网上我没找到..有的话或者我这个想法有问题麻烦告一下我 谢谢亲
*/
for(int i=2;i<m;i++){
if(!vis[i])//先判断当前数是否为素数
for(int j=i*2;j<=MAX_N;j=j+i)
vis[j]=true;//所有是i倍数的数肯定不是素数
}
}
因为内存的问题 差不多1000万多就是打表的上限了
如果遇到更大的数就需要用到MR大数检测了
//速度更快的线性筛法
int prime[100001],mark[1000001];
int tot,phi[100001];
void getphi(int N){
phi[1]=1;//φ(1)=1
for(int i=2;i<=N;i++){
if(!mark[i]){
prime[++tot]=i;
phi[i]=i-1;
}
for(int j=1;j<=tot;j++){
if(i*prime[j]>N) break;
mark[i*prime[j]]=1;
if(i%prime[j]==0){
phi[i*prime[j]]=phi[i]*prime[j];break;
}
else phi[i*prime[j]]=phi[i]*phi[prime[j]];
}
}
}
MR大数检测之前了解的时候是队友安利的,当时直接用的模板
既然是别人的东西就不粘了233,自行百度吧
2.辗转相除gcd
多写几遍就背出来了……
int gcd(int x,int y){
return y ? gcd(y,x%y):x;
}
3.欧拉函数
和1一样 分清楚什么时候打表什么时候直接求解
//直接求解欧拉函数
int euler(int n){ //返回euler(n)
int res=n,a=n;
int temp=sqrt(a);//之前的模板这里存在重复计算
for(int i=2;i<=temp;i++){
if(a%i==0){
res=res/i*(i-1);//防止数据溢出
while(a%i==0) a/=i;
}
}
if(a>1) res=res/a*(a-1);
return res;
}
//筛选法打欧拉函数表
#define Max 1000001
int euler[Max];
void Init(){
euler[1]=1;
for(int i=2;i<Max;i++)
euler[i]=i;
for(int i=2;i<Max;i++)
if(euler[i]==i)
for(int j=i;j<Max;j+=i)
euler[j]=euler[j]/i*(i-1);//防止数据溢出
}
4.快速幂
注意时刻取余,老丢…
极限点可以写ull 不过应该没题卡这个吧..
//如果需要实用快速幂说明n的大小偏大,所以非常有可能会溢出,故时刻取模是非常有必要的
typedef long long ll;
ll quick_pow(ll a,ll n,ll mod){
if(n==0)
return 1%mod;//如输入1 0 1的话 这里不取模会死==
ll x=quick_pow(a,n/2,mod);//虽然是递归不过因为是指数增长,深度并不是很深
ll ans=x*x%mod;
if(n%2==1)
ans=ans*a%mod;
return ans;
}
ll quick_pow(ll a,ll n,ll mod){//递归总的来说没有循环快
ll ans=1;
while(n>0){
if(n&1){
ans*=a;
ans%=mod;
}
a*=a;
a%=mod;
n>>=1;
}
return ans%mod;
}
5.扩展欧几里得
一定存在ax+by=gcd(a,b),事实上开个longlong保险些,被坑惨了..
这个我当时是看的这篇博客理解的:戳我
证明过程都在里面,这个就不说了
int a,b,x,y;//这里用了全局变量,所以调用就减少了x和y
void exgcd(int a,int b){
if(!b){
x=1;
y=0;
return;
}
exgcd(b,a%b);
int kkz=x;
x=y;
y=kkz-a/b*y;
return;//之前这里少写了一个return;
}
内容来说还是太少了,以后应该不断再添加吧,不过就是暑假集训的事情了,溜了溜了