数学杀我
欧拉函数
对于一个正整数
,小于
且和
互质的正整数的个数,记做
。其中
被定义为
。
部分性质:
- 若 是质数, 。
- 若 是质数 的 次幂,即 ,有 。
- 欧拉函数是积性函数(若 互质,则 )。
根据通式,枚举
的质因数。可枚举
以内的质因数,若存在大于
的质因数则额外处理。
时间复杂度:
int phi(int x){
int ans=x;
for(int i=2;i*i<=x;i++)if(!(x%i)){
ans-=ans/i;
while(!(x%i))x/=i;
}
if(x>1)ans-=ans/x;
return ans;
}
欧拉筛
Eratosthenes筛法筛质数:先假设所有的数都是质数。从小到大讨论,若
是质数,则
的倍数全部标记为合数。
用同样的方法求欧拉函数,先设
,若
是质数,则
的倍数(即包含
这个质因数的数)的函数值都要更新。
时间复杂度:
void make_prime(bool*mk,int n){
memset(mk,0,sizeof(mk));mk[1]=1;
for(int i=2;i<=n;i++)if(!mk[i])
for(int j=i+i;j<=n;j+=i)mk[j]=1;
}
void make_phi(int*phi,int n){
for(int i=1;i<=n;i++)phi[i]=i;
for(int i=2;i<=n;i++)if(phi[i]==i)
for(int j=i;j<=n;j+=i)phi[j]=phi[j]/i*(i-1);
}
Euler筛法筛质数:每个合数仅被它的最小质因数筛去正好一次。
线性筛求欧拉函数利用了欧拉函数的一些性质(
为质数):
- 若 ,那么
- 若 ,那么
时间复杂度:
void make_prime(bool*mk,int*p,int n){
memset(mk,0,sizeof(mk));mk[1]=1;
for(int i=2;i<=n;i++){
if(!mk[i])p[++tot]=i;
for(int j=1;j<=tot&&i*p[j]<=n;j++){
mk[i*p[j]]=1;
if(!(i%p[j]))break;
}
}
}
void make_phi(int*phi,bool*mk,int*p,int n){
memset(mk,0,sizeof(mk));mk[1]=phi[1]=1;
for(int i=2;i<=n;i++){
if(!mk[i])p[++tot]=i,phi[i]=i-1;
for(int j=1;j<=tot&&i*p[j]<=n;j++){
mk[i*p[j]]=1;
if(i%p[j])phi[i*p[j]]=phi[i]*(p[j]-1);
else{phi[i*p[j]]=phi[i]*p[j];break;}
}
}
}
GCD的个数(NKOJ 3684)
问题描述
给定两个正整数 , , 求满足下列两个条件的 的个数:
条件1:
条件2:
输入格式
一行,两个整数 和
输出格式
一行,一个整数,表示所求结果
样例输入
10 2
样例输出
6
提示
先考虑满足
且
的
的个数,显然
为
内的与
互质的数,总共有
个。
于是只需枚举
的因数
,当
时答案增加
。由于
较大,可以把枚举量减少到
。特殊情况特殊处理。
typedef long long LL;
LL phi(LL x){
LL ans=x;
for(LL i=2;i*i<=x;i++)if(!(x%i)){
ans=ans-ans/i;
while(!(x%i))x=x/i;
}
if(x>1)ans=ans-ans/x;
return ans;
}
int main(){
LL n,m,ans=0;
read(&n,&m);
for(LL d=1;d*d<=n;d++){
if(d>=m&&!(n%d))ans+=phi(n/d);
if(d*d<n&&!(n%d)&&n/d>=m)ans+=phi(d);
}
write(ans,"\n");
return 0;
}
找互质(NKOJ 4494)
问题描述
给一个整数 ,请你在指定区间 中找出共有多少个数是与 互质的。
输入格式
一行,三个整数 。
输出格式
一行,一个整数,表示计算结果。
样例输入
1 10 2
样例输出
5
提示
求区间
中与
互质的数的个数,可以分别求
和
中与
互质的数的个数。
如何求区间
中与
互质的数的个数?在
个数中除去与
有共同质因数的数,剩下的就是与
互质的数。
所以先将
分解质因数(求欧拉函数时会枚举质因数)。答案先设为
,假设每个数都与
互质;对于每一种质因数的组合
,答案减去
,就除去了
中
的倍数。枚举所有组合,根据容斥原理奇加偶减即可求出答案。
代码中用 位二进制表示是否选取质因数集合中的各个元素。
typedef long long LL;
LL pfact[50],tot=0,a,b,n;
void make_primefact(LL n){
for(LL i=2;i*i<=n;i++)if(!(n%i)){
pfact[++tot]=i;
while(!(n%i))n/=i;
}
if(n>1)pfact[++tot]=n;
}
LL calc(LL x){
LL ans=x,s=1ll<<tot;
for(LL i=1;i<s;i++){
LL t=i,it=1,d=1,cnt=0;
while(t){
if(t&1)d*=pfact[it],cnt++;
it++,t>>=1;
}
ans+=x/d*((cnt&1)?-1:1);
}
return ans;
}
int main(){
read(&a,&b,&n);
make_primefact(n);
write(a>b?0:calc(b)-calc(a-1),"\n");
return 0;
}
费马小定理
如果
是素数,且
与
互质,即
,那么
证明:
有小于质数
的
个数构成的集合
,显然
中的数都与
互质。
将集合
中的数乘以
并对
取模,放入集合
,于是
中的数都小于
,并且由于
,这些数都与
互质,得到
。
将两个集合的元素分别相乘得
。
求乘法逆元
因为
,那么
所以
就是
关于模
的乘法逆元。
欧拉定理
费马小定理是欧拉定理的一种特殊情况。
若
为正整数,且
互质,即
,则有:
证明过程参考费马小定理。
与
互质时,可用欧拉定理降幂。
一般地,
时:
上帝与集合的正确用法(NKOJ 5076)
问题描述
根据一些书上的记载,上帝的一次失败的创世经历是这样的:
第一天, 上帝创造了一个世界的基本元素,称做元。
第二天, 上帝创造了一个新的元素,称作 。 被定义为元构成的集合。容易发现,一共有 种不同的 。
第三天, 上帝又创造了一个新的元素,称作 。 被定义为 构成的集合。容易发现,一共有 种不同的 。
第四天, 上帝创造了新的元素 , 被定义为 的集合。显然,一共会有 种不同的 。
如果按照这样下去,上帝创造的第四种元素将会有 种,第五种元素将会有 种。这将会是一个天文数字。
然而,上帝并没有预料到元素种类数的增长是如此的迅速。他想要让世界的元素丰富起来,因此,日复一日,年复一年,他重复地创造着新的元素……
然而不久,当上帝创造出最后一种元素 时,他发现这世界的元素实在是太多了,以致于世界的容量不足,无法承受。因此在这一天,上帝毁灭了世界。
至今,上帝仍记得那次失败的创世经历,现在他想问问你,他最后一次创造的元素 一共有多少种?
上帝觉得这个数字可能过于巨大而无法表示出来,因此你只需要回答这个数对 取模后的值即可。
你可以认为上帝从 到 一共创造了 次元素,或 次,或者干脆 次。
一句话题意:求 对 取模后的值。
输入格式
接下来 行,每行一个正整数 ,代表你需要取模的值。
输出格式
行,每行一个正整数,为答案对 取模后的值。
样例输入
3
2
3
6
样例输出
0
1
4
提示
这是一道休闲题。
typedef long long LL;
LL phi(LL x){
LL ans=x;
for(LL i=2;i*i<=x;i++)if(!(x%i)){
ans-=ans/i;
while(!(x%i))x/=i;
}
if(x>1)ans-=ans/x;
return ans;
}
LL Pow(LL a,LL b,LL c){
LL ans=1;a%=c;
while(b){
if(b&1)ans=(ans*a)%c;
a=(a*a)%c,b>>=1;
}
return ans;
}
LL calc(LL p){return p==1?0:Pow(2,calc(phi(p))+phi(p),p);}
int main(){
LL t,p;
read(&t);
while(t--){read(&p);write(calc(p),"\n");}
return 0;
}