数论1 素数+约数+反素数
素数:
素数是指只能被自身整除和被1整除的数(大于1的自然数,1不是素数)不是素数的数为合数
常见的题型有:素数的判定+素数的筛选两种题型
- 素数的判定:
- 试除法(如果n不能被【0,根号n】内的所有数整除,那么就为素数)
- 加速技巧:>4的素数总是会被 6x+1 或 6x-1整除
- 素数的筛选:
- 素数埃氏塞法(复杂度:n* log log n,素数分布:x/ln x)代码略(不断塞出素数的倍数)
- 线性塞法(欧拉塞法)(复杂度 n) 常用
const int N=1000001;
int primes[N],cnt;//primes存素数 cnt 存素数的个数
bool st[N];//存数n是否被筛过
void getprimes(int n)
{
memset(st,false,sizeof st);//每个数都没有被筛过,初始化
cnt=0;
for(int i=2;i<=n;i++)
{
if(!st[i])
primes[cnt++]=i;//没有被筛过就是素数
for(int j=0;j<cnt && i*primes[j]<=n;j++)//
{
st[i*primes[j]]=true;
if(i%primes[j]==0)
break;
}
}
}
约数:
若整数n除以整数d的余数是0 ,即d能整除n,则称d是n的约数, n是d的倍数
对于一个数我们可以质因数分解,所以对于每个数n
我们可以分解成 p1 ^ c1+p2 ^ c2+……+px ^ cx
而约数个数也就是(c1+1) * (c2+1) * …… (cx+1)
而约数和就是 (1+p1+p1^ 2 +p1^ 3+p1^ 4……+p1^ c1) (1+1+p2+p2^ 2+p2^ 3+p2^ 4…… +p2^ c2)……(1+pn+pn^ 2+pn^ 3……pn^ cn)
- 约数个数
对于线性筛选,我们其实就是在筛选质数的时候顺便求出约数个数,我们知道线性筛选就是筛出质数然后再枚举最小质因子去进行筛选。我们先定义两个数组t,和e,t[i]表示i这个数约数个数,e[i]表示i这个质因数分解后最小质因子的次方,即c1。
我们对x进行分类讨论,如果x是质数的话,那么t[x]=2,e[x]=1。这个就是质数的因子只有自己和1嘛,e[x]=1是因为1不是质数,所以i的最小质因子是自己,这个当然就是1了。
然后我们就到了筛质数的环节了,在这里我们会处理x不是质数的情况,对于x我们可以在筛时这么写i*prime[j](这个是筛质数的操作,prime数组是存质数的),所以我们还要对i和prime[j]进行分类讨论。
对于i是prime[j]的倍数,那么t[x]=t[i]/(e[i]+1) * (e[i]+2),为什么是这个?我们先看e数组把,因为和e数组有关,因为i是prime[j](我们是枚举最小质因子去筛的)的倍数所以x的最小质因子指数会多1,所以e[x]=e[i]+1,所以我们根据之前给的约数个数公式可以知道其实就是除以(e[i]+1),乘以(e[i]+2),这就是上面公式的由来。
对于i不是prime[j]的倍数,那么e[x]=1,因为prime[j]是我们枚举的最小质因子,所以这个只有一个。因为约数个数这个函数是积性函数,所以t[x]=t[i] * t[prime[j]],因为t[prime[j]]的值一定是2,因为prime[j]是质数,所以我们可以简化写成t[x]=t[i] * 2。
这样我们就讨论完了。
#include<iostream>
#include<cstdio>
using namespace std;
long long prime[3000100],t[3000100],e[3000100];
int vist[3000100];
int main()
{
int n,num=0;
scanf("%d",&n);
t[1]=1;//初值,1的约数个数是1
for(int i=2;i<=n;i++)
{
if(vist[i]==0)//质数的话
{
prime[++num]=i;
t[i]=2;
e[i]=1;
}
for(int j=1;j<=num;j++)//用最小质因子去筛
{
if(i*prime[j]>n)//超过了n就不用了
break;
vist[i*prime[j]]=1;//打下标记是合数
if(i%prime[j]==0)//分类讨论
{
t[i*prime[j]]=t[i]/(e[i]+1)*(e[i]+2);//根据我们的公式计算
e[i*prime[j]]=e[i]+1;
break;
}
else //不是倍数的时候
{
t[i*prime[j]]=t[i]*2;
e[i*prime[j]]=1;
}
}
}
for(int i=1;i<=n;i++)
printf("%lld\n",t[i]);//输出答案
return 0;
}
约数和
既然有约数个数,那么跟定有约数和,现在我们就来讲一下怎么计算约数和。
根据上面约数个数的思想,我们也用分类讨论来计算约数和。
同样我们要定义两个数组,一个是t,t[i]表示i的约数和,同样我们也要一个辅助的数组e,e[i]表示不能被最小质因子整除的约数的和。
首先如果x是质数的话,约数只有自己和1,那么t[x]=x+1,而不能被最小质因子整除的数也只有1,所以e[x]=1。
然后我们再用i*prime[j]去表示非质数的x,同样,也分i可以整除prime[j]和不能整除。
不可以整除的话,意味着t[i]和t[prime[j]]这两个是没有重复部分的,因为他们这两个数是互质的,证明一下吧,因为prime[j]是个质数,所以如果不是互质的话只有一种可能,就是i是prime[j]的倍数(这就是我们要这么分类讨论的原因,上面的分类讨论也是因为这个)。所以t[x]=t[i]*prime[j]+t[i],化简一下就是t[x]=t[i] * (prime[j]+1)。而e因为不是倍数所以e[x]=t[i]
如果i是prime[j]的倍数的话因为他们中有重复的,重复的部分就是可以被最小质因子整除的约数和,也就是t[i]-e[i],那么我们为什么不用e数组直接存这个呢?因为不方便计算啊。所以我们就是t[x]=t[i] * prime[j]+t[i]-(t[i]-e[i]),化简一下就是t[x]=t[i]*prime[j]+e[i],而e因为是倍数所以我们要继承一下e[x]=e[i],这样就计算完了。
#include<iostream>
#include<cstdio>
using namespace std;
long long vist[3000100],prime[3000100],t[3000100],e[3000100];
int main()
{
int n,num=0;
scanf("%d",&n);
t[1]=1;//边界
for(int i=2;i<=n;i++)
{
if(vist[i]==0)//质数的情况
{
prime[++num]=i;
t[i]=i+1;
e[i]=1;
}
for(int j=1;j<=num;j++)
{
if(i*prime[j]>n)
break;
vist[i*prime[j]]=1;//打个标记
if(i%prime[j]==0)//如果是倍数
{
t[i*prime[j]]=t[i]*prime[j]+e[i];
e[i*prime[j]]=e[i];
break;
}
else//不是倍数
{
t[i*prime[j]]=t[i]*(prime[j]+1);
e[i*prime[j]]=t[i];
}
}
}
for(int i=1;i<=n;i++)
printf("%lld\n",t[i]);//输出
return 0;
}
反素数
#include <iostream>
#include <stdio.h>
using namespace std;
typedef unsigned long long ull;
#define INF ((ull)1)<<62
const int maxn = 50000;
ull ans;
int Type,K;
int d[maxn];
int prime[] = {2,3,5,7,11,13,17,19,23,29,31,37,41,47,53};
void create_table()
{
for(int i = 1; i < maxn; i++)
d[i] = i;
for(int i = 1; i < maxn; i++)
{
for(int j = i; j < maxn; j += i)
d[j]--;
if(!d[d[i]])
d[d[i]] = i;
d[i] = 0;
}
}
void dfs(int pos,ull value,int num)
{
if(num > K || pos > 15)
return;
if(num == K)
{
if(value <ans)
ans=value;
return;
}
for(int i = 1; i <= 63; i++)
{
if(value > ans/prime[pos] || num*(i+1)>K)
break;
value *= prime[pos];
if(K%(num*(i+1))==0)
dfs(pos+1,value,num*(i+1));
}
}
int main()
{
int T,t=1;
create_table();
cin>>T;
while(T--)
{
cin>>Type>>K;
cout<<"Case "<<t++<<": ";
if(Type)
{
if(d[K])
cout<<d[K]<<endl;
else
cout<<"Illegal"<<endl;
}
else
{
ans = 1e19;
dfs(0,1,1);
if(ans > INF)
cout<<"INF"<<endl;
else
cout<<ans<<endl;
}
}
return 0;
}