欧拉函数板子
线性素数筛
-
1 不管i是不是素数,它与素数的乘积一定不是素数,直接use =1;
-
如果use=0那么i是素数加入到ss表中,然后枚举素数表,执行操作1,如果i是合数,如果素数表中有它的质因数,则说明已经被质因数筛过了,直接break即可。
void pd()
{
use[1]=1;
for(int i=2;i<=n;i++)
{
if(use[i]==0);
a[++k]=i;
for(int j=1;j<=k&&i*a[j]<=n;j++)
{
use[i*a[j]]=1;
if(i%a[j]==0)
break;
}
}
return ;
}
线性求欧拉函数
什么是欧拉函数,欧拉定理?
欧拉函数性质:
-
因为欧拉函数是积性函数雾,如果i与p互质则有 ph (i*p) =ph i *ph p (i%p!=0)
-
ph i*p=ph i* p (i%p==0)不论p是不是质数
这样就可以枚举出所有情况下的欧拉函数
边线性筛边算欧拉函数:
void get_phi()
{
for(int i=1;i<=N-3;i++)
phi[i]=i;
use[1]=1;
for(int i=2;i<=N-3;i++)
{
if(use[i]==0)
{
ss[++k]=i;
phi[i]=i-1;
}
for(int j=1;j<=k&&i*ss[j]<=N-3;j++)
{
int temp=i*ss[j];
use[temp]=1;
if(i%ss[j]==0)//i是ss的倍数
{
phi[temp]=phi[i]*ss[j];
break;
}
else
{
phi[temp]=phi[i]*(ss[j]-1);
}
}
}
}
求单个的欧拉函数
inline long long phi(long long x)
{
long long s=x,i=2;
for (;i*i<=x;i++) if (!(x%i)) {s=s/i*(i-1); for (;!(x%i);x/=i);}
if (x>1) s=s/x*(x-1); return s;
}
乘法逆元
其实乘法逆元就相当于除法a变为乘a分之一基础模板题目这里
贴代码吧
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
using namespace std;
const int N=3e6+6;
long long n,p,inv[N];
int main()
{
scanf("%lld %lld",&n,&p);
inv[1]=1;
inv[0]=0;
for(int i=2;i<=n;i++)
{
inv[i]=(p-(p/i)*inv[p%i]%p)%p;
}
for(int i=1;i<=n;i++)
printf("%lld\n",inv[i]);
return 0;
}
下面是对逆元的证明:
\[p=k*i+r;这里的p是mod,i\in [2,n]\\
k=p/i; \quad r=p\%i\\
k*i+r=0 \quad mod p\\
k*inv[r]+inv[i]=0\quad mod p\\
等式两边同时乘上i,r的逆元\\
p/i*inv[p\%i]+inv[i]=0 \quad 为避免出现负数所以加一个p\\
inv[i]=(p-p/i*inv[p\%i]\%p)\%p \\
\]
还有一种求逆元的方法
如果mod是质数的话则
a-1 =a(p-2) mod p 而这个可以用快速幂优化
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<vector>
using namespace std;
const int N=5e6+6;
long long maxn,n,p,temp=1,s[N],k,sinv,sum,a[N],inv[N];
long long read()
{
int ans=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
w=-1;//注意这里一定不能写成了 ,
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
ans=(ans<<1)+(ans<<3)+ch-'0';
ch=getchar();
}
return ans*w;
}
long long pow3(long long a,long long b)
{
long long ans=1;
a=a%p;
while(b)
{
if(b&1)
ans=ans*a%p;
a=a*a%p;
b=b>>1;
}
return ans%p;
}
int main()
{
//freopen("niyuan.in","r",stdin);
//freopen("niyuan.out","w",stdout);
n=read();p=read();k=read();
s[0]=1;
for(int i=1;i<=n;i++)
{
a[i]=read();
s[i]=s[i-1]*a[i]%p;
}
sinv=pow3(s[n],p-2)%p;
for(int i=n;i>=1;i--)
{
inv[i]=sinv*s[i-1]%p;
sinv=sinv*a[i]%p;
}
for(int i=1;i<=n;i++)
{
temp=temp*k%p;
sum=(sum+temp*inv[i]%p)%p;
}
printf("%lld\n",sum%p);
return 0;
}
gcd和exgcd
顺便贴一下exgcd吧(gcd可以用__gcd(a,b)这个自带函数)
扩欧是求符合 ax+by=c的最小整数解顺便求出gcd(a,b)
函数的参数a, b即为等式当中的两个参数,x和y传引用,可以从过程当中求出x和y,返回值是gcd(a, b)。
#include <cstdio>
#include <cstring>
using namespace std;
long long extend_gcd(long long a, long long b, long long &x, long long &y)
{
if(b == 0)
{
x = 1, y = 0;
return a;
}
else
{
long long r = extend_gcd(b, a%b, y, x);
y -= x*(a/b);
return r;
}
}
$道路千万条,点赞第一条;阅读不规范,笔者两行泪$