欧拉函数板子

线性素数筛

  • 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;
    }
}
posted @ 2020-11-04 22:17  Sakura_Momoko  阅读(96)  评论(0编辑  收藏  举报