一、欧拉函数
1、欧拉函数求n以内与n互素数的个数与原根个数( erla(erla(n)) )
例题:
poj 1284
g^i mod p ≠ g^j mod p (p为素数),其中i≠j且i, j介于1至(p-1)之间,则g为p的原根。
int erla(int n)
{
int sum=n;
for(int i=2;i<=sqrt(n);i++)
{
if(n%i==0)
{
n/=i;
sum=sum/i*(i-1);
while(n%i==0)
n/=i;
}
}
if(n>1)
sum=sum/n*(n-1);
return sum;
}
二、欧几里得
1、欧几里得求gcd
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
2、扩展欧几里得
例题:
poj 1061
///ax+by=gcd(a,b)
///ax0+by0=gcd(b,a%b)
int exgcd(ll a,ll b,ll &x,ll &y)///引用x,y
{
if(b==0)
{
x=1; y=0;
return a;
}
ll t=exgcd(b,a%b,x,y);
ll xx=x;
x=y;
y=xx-a/b*y;
return t;
}
三、素数筛
1、埃式筛法(优化)
void isprime()
{
s[1]=1;///1不是素数
for(int i=2;i<=sqrt(N);i++)
{
if(s[i]==0)
{
for(int j=i;j*i<=N;j++)
s[j*i]=1;
}
}
}
2、欧拉筛法
void isprime()
{
s[1]=1;///1不是素数
int cnt=0;
for(int i=2;i<=N;i++)
{
if(s[i]==0)
prime[cnt++]=i;
for(int j=0;j<cnt,i*prime[j]<=N;j++)
{
s[i*prime[j]]=1;
if(i%prime[j]==0)
break;
}
}
}
四、快速幂
1、二分快速幂
ll ksm(ll a,ll b,ll mod)
{
ll base=a,res=1;
while(b)
{
if(b&1)
res=(base*res)%mod;
b>>=1;
base=(base*base)%mod;
}
return res;
}
2、矩阵快速幂
Fibonacci 数列:
F(n) = F(n-1) + 2F(n-2) + n4
分步拆解问题
首先把非线性递推式转换为线性递推式
∵ (n+1)4 = n4 + 4n3 + 6n2 + 4n + n0
∵ F(n+1) = F(n) + 2F(n-1) + (n+1)4
∴ F(n+1) = F(n) + 2F(n-1) + n4 + 4n3 + 6n2 + 4n + n0
转自:https://www.jianshu.com/p/25eba927d9da
const int N=15;
struct mat{
int d[N][N];
int n;
};
mat muti(mat a,mat b,int mod)
{
mat temp;
memset(temp.d,0,sizeof(temp.d));
for(int i=0;i<a.n;i++)
{
for(int j=0;j<b.n;j++)
{
for(int k=0;k<a.n;k++)
temp.d[i][j]=(temp.d[i][j]+(a.d[i][k]*b.d[k][j])%mod)%mod;
}
}
temp.n=a.n;
return temp;
}
mat mat_ksm(mat a,int k,int mod)
{
mat res;
memset(res.d,0,sizeof(res.d));
for(int i=0;i<a.n;i++)
res.d[i][i]=1;
res.n=a.n;
while(k)
{
if(k&1)
res=muti(res,a,mod);
k>>=1;
a=muti(a,a,mod);
}
return res;
}
五、 大数运算
1、大数加法
string add(string a,string b)
{
int lena=a.size(),lenb=b.size();
int carry=0;///进位值初始为0
string ans;
for(int i=lena-1,j=lenb-1;i>=0||j>=0;i--,j--)///从末尾往前加,位不足补0
{
int sum=(i>=0?a[i]-'0':0)+(j>=0?b[j]-'0':0)+carry;
carry=sum/10;
ans.push_back(sum%10+'0');
}
if(carry)///最后的进位加上去如6+4=10,进位1最后要处理
ans.push_back(carry+'0');
reverse(ans.begin(),ans.end());///翻转
while(ans.size()>1&&ans[0]=='0') ans.erase(ans.begin());///去除前导0
return ans;
}
2、大数减法
string subtract(string a,string b)
{
if(a==b) ///一样直接等0
return "0";
string ans;
int flag=1;
if(a.size()<b.size()) ///判断符号位
flag=0,swap(a,b);
if(a.size()==b.size()&&a<b) ///判断符号位
flag=0,swap(a,b);
int lena=a.size(),lenb=b.size();
for(int i=lena-1,j=lenb-1;i>=0||j>=0;i--,j--)///从末尾往前加,位不足补0
{
int sum=a[i]-'0'-(j>=0?b[j]-'0':0);
if(sum<0)
sum+=10,a[i-1]-=1;
ans.push_back(sum+'0');
}
for(string::iterator it=ans.end()-1;it!=ans.begin();it--)///删除前导0
{
if(*it=='0')
ans.erase(it);
else
break;
}
if(flag==0)///符号位
ans.push_back('-');
reverse(ans.begin(),ans.end());///翻转
return ans;
}
3、大数乘法
string mulit(string a,string b)
{
if(a=="0"||b=="0")
return "0";
int t[10005]={0};
string ans;
int lena=a.size(),lenb=b.size();
for(int i=0;i<lena;i++)///乘法
for(int j=0;j<lenb;j++)
t[i+j+1]+=(a[i]-'0')*(b[j]-'0');
for(int i=lena+lenb-1;i>=1;i--)///进位
{
t[i-1]+=t[i]/10;
t[i]%=10;
}
if(t[0]!=0)///第一位判0
ans.push_back(t[0]+'0');
for(int i=1;i<lenb+lena;i++)///装入ans返回
ans.push_back(t[i]+'0');
return ans;
}
string muti_xiao(string s,int n)///大数乘以int内的数
{
int len=s.size(),temp;
int carry=0;
string ans;
for(int i=0;i<len;i++)
{
temp=(s[i]-'0')*n+carry;
ans.push_back(temp%10+'0');
carry=temp/10;
}
while(carry)
ans[len++]=carry%10+'0',carry/=10;
reverse(ans.begin(),ans.end());
return ans;
}
4、大数求模
int bignum_mod(string a,int mod)///求模公式(上一位mod后的值*10+当前位)%mod
{
int len=a.size();
int ans=(a[0]-'0')%mod;
for(int i=1;i<len;i++)
ans=(ans*10+(a[i]-'0'))%mod;
return ans;
}
5、大数除法(一个大数除以一个一位数)
bool div(string &s,int n)///返回是否可以被整除,s会变成商
{
string temp;
int len=s.size();
int x=0;
for(int i=0;i<len;i++)///从最高位开始模拟除法
{
x=x*10+(s[i]-'0');
//cout<<x<<endl;
temp.push_back(x/n+'0');
x%=n;
}
if(x==0)///可以除尽就将s变为商
{
while(temp[0]=='0'&&temp.size()>1)
temp.erase(temp.begin());
s=temp;
return 1;
}
return 0;///除不尽返回0
}
六、全排列
1、自己函数实现
void dfs(int beginn,int endn)
{
if(beginn==endn)
{
for(int i=0;i<=endn;i++)
cout<<a[i]<<" ";
cout<<endl;
}
for(int i=beginn;i<=endn;i++)
{
swap(a[beginn],a[i]);
dfs(beginn+1,endn);
swap(a[beginn],a[i]);
}
}
2、函数库实现
///函数功能是判断是否有比当前排列方式字典序更大的排列方式
///如果有排列数组并返回1,如果没有返回0
///可排string
///头文件#include<algorithm>
///缺陷,如果输入数组为2 1 3,函数就不会输出1 2 3等像这种比它字典序小的排列方式(排一次序可解决)
do{
for(int i=0;i<n;i++)
cout<<a[i]<<" ";
cout<<endl;
}while(next_permutation(a,a+n));///字典序输出。
七、组合数求模
///较小范围内
const int mod=1E9+7;
const int N=10000+5;
int comb[N][N];//comb[i][j]内存放的是C(i,j)%mod
void init() {
for(int i=0; i<N; i++) {
comb[i][i]=1;
comb[i][0]=1;
for(int j=1; j<i; j++) {
comb[i][j]=comb[i-1][j]+comb[i-1][j-1];
if(comb[i][j]>=mod)
comb[i][j]-=mod;
}
}
}
END、较杂的数论知识点
1、一个数的因子个数等于这个数分解质因式地质因子指数加一之积。
2、费马小定理
- 特别的,当p为素数时,x无法被p整除,φ§=p-1,于是便有费马小定理X^p-1≡1(mod p)
- 在p是素数时,对任意正整数x都有Xp≡X(mod p) 于是对于a的逆元x,有ax≡1(mod
m),对于a,m互素且m为素数时,有x=am-2,于是我们可以通过快速幂快速求出a的逆元。 - 另外,借助素数筛,我们还可以很快的求出1-n的欧拉函数值。每当我们找到一个素数,就把他的倍数的欧拉函数值乘上(p-1)/p.
- 而且,借助费马小定理我们可以实现对除法取模。
(A/B)%P= A%P X (1/B)%P = A%P X (B^-1)%P = A%P X ((B ^ P-1) ^-1X (B ^ P-2))%P
=A%P X ((B ^ P-1)^-1%P X (B ^ P-2)%P)%P*
B,P互质且P为素数所以
*=A%P X (1 X (B ^ P-2)%P)%P =A%P X (B ^ P-2)%P
a^p-1=a^p-2 *a
引用小费马定理 a^p-1 mod p = 1 mod p
把 a 移到上述等式的右边有 a^p-2 mod p = a^-1 mod p ,(个人理解成两边同乘a^-1 mod p)
所以有:(A/B)%P=A%P X (B ^ P-2)%P