欢迎这位怪蜀黍来到《从线性筛到欧拉函数,你十有八九能懂吧! - 童话镇里的星河 - 博客园》

从线性筛到欧拉函数,你十有八九能懂吧!

这篇文章的动机:万一有什么长进呢?(先痴想一下吧)
本文难度:普及-→提高+

Part1.线性筛

不止能筛质数,所有的积性函数都可以O(n)筛出。
ps:积性函数只对于x,yP(这里P是质数的集合),f(ab)=f(a)f(b)的数论函数。
当然不排除你喜爱埃氏筛,但洛谷1e8照样会卡你。
但不能略过:
埃氏筛,全称是啥显然忘了(用不到吧qwq?),核心思想是利用已有的质数,与当前的数相乘,得到的一定是合数。
下面是线性筛质数:

    judge[1]=true;
    for(int i=2;i<=maxn;i++)
    {
        if(i*i>maxn) continue;
        if(!judge[i])
        {
            for(int j=2*i;j<=maxn;j+=i) judge[j]=true;//是合数
        }
    }

一看就懂吧。
复杂度分析(并不会):
我们知道素数密度是nlnn的,那么对于每个合数x要筛maxnx次然而他求和是接近logn的(难死了),所以应该是O(nloglogn),近似于常数了(这改变不了他被卡的命运)
那大神欧拉发现(这谁都可以),有些数会被筛好几次,这就是他不能O(n)的原因,比如6,被2,3筛两次,那我们可以这样写:

    for(int i=2;i<=maxn;i++)
    {
    	if(!judge[i]) prime[++cnt]=i;
    	for(int j=1;j<=cnt&&(prime[j]*i)<=maxn;j++)
    	{
    		judge[i*prime[j]]=true;
    		if(i%prime[j]==0) break;
	}
    }

为什么只会筛一次呢?
考虑如果整除,那就有一个平方因子也可以筛掉他,丢到后面去就好了。
所以复杂度就是O(n)的,再卡就没办法了。

Part2.欧拉函数

定义欧拉函数ψ(n)为与n互质的数,一个大佬讲得贼好:
ψ(6)咋算?这里是可以枚举的,是O(nlogn),他讲了种好方法:
写出所有分母是6的不大于1的分数。

16,26,36,46,56,66

化简后分组:

16,13,12,23,56,11

那么:

ψ(6)=2,ψ(3)=2,ψ(2)=1,ψ(1)=1

然后就说明:

d|nψ(d)=n

显然观察归纳不严密,若我有幸学习并会了了莫反再来证吧。

Part3.线性求法

欧拉函数是积性函数,保证了它可以被O(n)筛出,但原理较复杂,建议先别学qwq,看完下面就好多了,反正我是

Part4.单个数的欧拉函数

考虑一个事:

xP,ψ(x)=x1

只有自身不互质,我们称为性质一。

性质二:考虑一个合数a满足a=pk(k>1,kZ),显然与他不互质的数b[p,2p,3p......pk1p](饶了我吧,我真不会打大括号)。
那么ψ(a)=pkpk1=pk(11p)

性质三:最一般的情况,每个合数a都可写成唯一分解式:

a=p1k1p2k2......pnkn

即:

a=i=1npiki

好在欧拉函数是个特殊的积性函数,满足ψ(nm)=ψ(n)ψ(m),成立(当然在定义域)。
那么

ψ(a)=i=1nψ(piki)=i=1npikii=1n(11pi)=ai=1n(11pi)

到这里,我们就可以O(n)得出 一个数的欧拉函数了。 其实思想和早先我们学习质数验证枚举因子是异曲同工的:
代码:

int phi(int n)
{
	int ans=n;
	for(int i=2;i<=sqrt(n);i++)
	{
		if(n%i==0)
		{
			ans=ans/i*(i-1);//这是一个p,注意先除再乘,防止炸int
			while(n%i==0) n/=i; //把质数次方因子筛没了,就不会错了
		}
	}
	if(n>=2) ans=ans/n*(n-1);//最后有可能剩下
	return ans;
}

不过有些人不这样写:

int phi(int n)
{
	int ans=1,now;
	for(int i=1;i<=sqrt(n);i++) 
	{
		now=1;
		if(n%i==0)
		{
          	now=i-1,n/=i;
			while(n%i==0) now*=i,n/=i;
		}
		ans*=now;
	}
	if(n!=1) ans*=n-1;
	return ans;

简单说下,对于一小部分(pk)考虑变形:

ψ(pk)=pkpk1=pk1(p1)

于是发现出现质数p先乘一次p1,再来k1p,其他都是一样的,不过这里:

now=i-1,n/=i;

注意除一下,然后now,ans分别记录,不能够混淆。

Part5.回归Part3

剩下的就好说了。

phi[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!vis[i]) p[++cnt]=i,phi[i]=i-1;//质数只有自身与自己不互质 
		for(int j=1;p[j]&&i*p[j]<=n;j++)
		{
			vis[i*p[j]]=1;
			if(!(i%p[j]))
			{
				phi[i*p[j]]=phi[i]*p[j];
				//这里是平方因子了,不要减1 
				break;
			}
			else phi[i*p[j]]=phi[i]*(p[j]-1);//第一次出现因子,乘p-1 
		}
	}

精华都在代码里了。
其实和筛素数的是一样的。
线性筛可是很NB的,以至于所有积性函数都怕他(掩盖不了看似大的惊人的复杂度了)。

Part6 欧拉定理

前置知识:快速幂。
大概这样写,复杂度是O(logn)的。

ll quickpow(ll a,ll b)
{
    ll ans=1,base=a;
    while(b!=0)
	{
        if(b&1!=0)//b%2==1;
		{
            ans*=base;
        }
        base*=base;
        b>>=1;//b/=2;
    }
    return ans;
}

你甚至可以自己定义乘法运算跑快速幂。
可是指数很大怎么办呢?
O(logn)都过不去啊!
伟大的欧拉提出了定理,我们为了纪念他,称为欧拉定理

gcd(a,m)=1,则:

aψ(m)1(modm)

但他认为不够,又提出了扩展欧拉定理

ab{abb<ψ(m)abmodψ(m)+ψ(m)bψ(m)

这样,先预处理出ψ(m),再用字符串边读边模即可。
复杂度大概O(lenb+m+logm)(毕竟ψ(m)上界是m),可以通过本题。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int phi(int x)//欧拉函数
{
	int ans=1,num=1;
	for(int i=2;i*i<=x;i++)
	{
		if(!(x%i))
		{
			num=i-1,x/=i;
			while(!(x%i)) num=num*i,x/=i;
			ans=num*ans;
		}
	}
	if(x!=1) ans=ans*(x-1);
	return ans;
}
inline int read(int mod)//改进快读,让他边读边输入
{
	//g用来判断b与phi(m)的大小,如果小于,就不能加了,这是坑点!
	int x=0;
	bool g=false;
	char c=getchar();
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9')
	{
		x=(x<<3)+(x<<1)+(c^'0');
		if(x>=mod) x%=mod,g=true;
		c=getchar();
	}
	if(g) return (x+mod);
	else return x;
}
int a,mod;
char b[20000005];
inline int quickpow(int a,int b)//快速幂
{
	long long ans=1,base=(long long)a;
	while(b)
	{
		if(b&1) ans=ans*base%mod;
		b>>=1;
		base=base*base%mod;
	}
	return (int)(ans%mod);
}
int p;
int main()
{
	scanf("%d%d",&a,&mod);
	int p=phi(mod);
	int cishu=read(p);//得出的化简次数
	int s=quickpow(a,cishu);
	printf("%d\n",s);
	return 0;
}

大概就这些啦,还有些高深点的东西以后再更。

posted @   童话镇里的星河  阅读(881)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示