欧拉函数的一些题
1.P2158 [SDOI2008] 仪仗队
题目大意
在一个从
分析
一个点
那么什么时候
发现
则
(若
那么就有
这个式子其实可以莫反,不过没必要
把能看到的点画出来,可以发现它们关于
而
表示表示小于等于
恰好是
则
(点
code
#include<bits/stdc++.h>
#define il inline
#define cs cosnt
#define ri register
using namespace std;
const int N=40001;
int fi[N],b[N],cnt,ss[664580],mx;
inline int done(const int &n){
if(!n) return 0;//
register int ans=3;
for(register int i=2;i<=n;++i){
if(!b[i]) ss[++cnt]=i,fi[i]=i-1;
for(register int j=1;j<=cnt&&ss[j]*i<=n;++j){
b[i*ss[j]]=1;
if(i%ss[j]) fi[i*ss[j]]=fi[i]*fi[ss[j]];
else{fi[i*ss[j]]=fi[i]*ss[j];break;}
}
ans+=2*fi[i];
}
return ans;
}
int main(){
int n;
cin>>n;
cout<<done(n-1);
return 0;
}
2.P5091 【模板】扩展欧拉定理
题目大意
求
分析
扩展欧拉定理的模板题,不过要注意
code
#include<bits/stdc++.h>
#define il inline
#define ri register
#define cs const
using namespace std;
namespace Q{
il int rd(){
ri int x=0,f=1;ri char c=getchar();
while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
return x*f;
}
il void wt(int x){
if(x<0) x=-x,putchar('-');
if(x>=10) wt(x/10);
return putchar(x%10+48),void();
}
il int qpow(int a,int b,int p){
ri int as=1;
while(b>0){
if(b&1) as=1ll*as*a%p;
a=1ll*a*a%p,b>>=1;
}
return as;
}
il int varphi(int p){
ri int res=p;
for(ri int i=2;i*i<=p;++i){
if(p%i==0){
res=res/i*(i-1);
while(p%i==0){
p/=i;
}
}
}
if(p>1) res=res/p*(p-1);
return res;
}
il int mo(int mod){//读入的时候顺便取模
ri int x=0,f=0;ri char c=getchar();
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9'){
x=x*10+(c^48);
if(x>mod) x%=mod,f=1;//判断一下b是不是大于\varphi(m)
c=getchar();
}
return x+f*mod;//如果大于就加\varphi(m)
}
} using namespace Q;
int a,m,b;
signed main(){
a=rd(),m=rd(),b=mo(varphi(m));
wt(qpow(a,b,m));
return 0;
}
3.P4139 上帝与集合的正确用法
题目大意
定义
分析
不难发现,就是要求
考虑扩展欧拉定理
这里指数
那么,对于每一层,都有
可以证明在
所以虽然
可以递归求解,边界
code
#include<bits/stdc++.h>
using namespace std;
inline void rd(int &x){
register bool f=x=0;register char c=getchar();
while(c<'0'||c>'9') f|= (c=='-'), c=getchar();
while(c>='0'&c<='9')x=x*10+(c^48),c=getchar();
}
inline void wt(int x){
if(x<0) putchar('-'),x=-x;
if(x>=10) wt(x/10);
putchar(x%10+48);
}
const int T=1e3+1,N=1e7+1;
int t,p[T],fi[N],b[N],cnt,ss[664580],mx;
inline void init(const int &n){
for(register int i=2;i<=n;++i){
if(!b[i]) ss[++cnt]=i,fi[i]=i-1;
for(register int j=1;j<=cnt&&ss[j]*i<=n;++j){
b[i*ss[j]]=1;
if(i%ss[j]) fi[i*ss[j]]=fi[i]*fi[ss[j]];
else{fi[i*ss[j]]=fi[i]*ss[j];break;}
}
}
}
inline int qpow(register int a,register int b,const int &m){
register int ans=1;
while(b){
if(b&1) ans=(1ll*ans*a)%m;
a=(1ll*a*a)%m,b>>=1;
}
return ans;
}
inline int done(const int &m){
if(m==1) return 0;
return qpow(2,done(fi[m])+fi[m],m);
}
int main(){
rd(t);
for(register int i=1;i<=t;++i){
rd(p[i]),mx=max(mx,p[i]);
}
init(mx);
for(register int i=1;i<=t;++i){
wt(done(p[i])),putchar('\n');
}
return 0;
}
4.Power Tower
题目大意
给定一个数列
分析
还是用扩展欧拉定理递归计算,注意判断指数是否大于
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100005;
int n,m,q,w[N];
map<int,int> fi;
inline int rd(){
register int x=0;register char c=getchar();
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
return x;
}
inline void wt(int x){
if(x>=10) wt(x/10);
putchar(x%10+48);
}
inline void getfi(int n){
register int ans=n,nn=n;
for(int i=2;i*i<=n;i++){
if(nn%i==0){
ans/=i;ans*=(i-1);
while(nn%i==0) nn/=i;
}
}
if(nn>1) ans/=nn,ans*=(nn-1);
fi[n]=ans;
}
inline int qpow(int a,int b,int p){
register int ans=1;
while(b>0){
if(b&1){
ans=ans*a;
if(ans>=p) ans=ans%p+p;
}
a=a*a;
if(a>=p) a=a%p+p;
b>>=1;
}
return ans;
}
inline int done(int i,int p,int r){
if(i>r||p==1) return 1;
return qpow(w[i],done(i+1,fi[p],r),p);
}
signed main(){
n=rd(),m=rd();
for(register int i=1;i<=n;i++) w[i]=rd();
register int ff=m;
while(ff!=1) getfi(ff),ff=fi[ff];fi[ff]=1;
q=rd();
register int l,r;
while(q--){
l=rd(),r=rd();
wt(done(l,m,r)%m),putchar('\n');
}
}
5.P2568 GCD
题目大意
求
分析
做法比较玄学
感谢理解一下,
令
则
考虑枚举
可以在欧拉筛时对每一个
于是
注意
复杂度
code
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
using namespace std;
cs int N=1e7+9;
int b[N],ss[N],tot,fi[N];
il void init(int n){
fi[1]=1;
for(ri int i=2;i<=n;++i){
if(!b[i]) ss[++tot]=i,fi[i]=i-1;
b[i]=tot;
for(ri int j=1;j<=tot&&ss[j]*i<=n;++j){
b[i*ss[j]]=1;
if(i%ss[j]) fi[i*ss[j]]=fi[i]*fi[ss[j]];
else{fi[i*ss[j]]=fi[i]*ss[j];break;}
}
}
}
int main(){
int n;
long long as=0;
cin>>n;
init(n);
for(ri int i=2;i<n;++i){
as+=fi[i]*b[n/i];
}
cout<<as*2+tot;
return 0;
}
6.P2398 GCD SUM
题目大意
求
分析
欧拉反演做法在莫比乌斯反演里提到过了,这里不再赘述
最终式子
复杂度
code(欧拉反演)
//题解上粘的代码
#include <cstdio>
#include <cstring>
const int MAXN = 100010;
int prime[MAXN], v[MAXN], phi[MAXN], sumPhi[MAXN], cnt, n;
void eular(int n) {//线性筛筛欧拉函数
memset(v, 0, sizeof(v));
sumPhi[1] = phi[1] = 1;
for (int i = 2; i <= n; i++) {
if (!v[i]) {
prime[++cnt] = v[i] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= cnt; j++) {
if (prime[j] > v[i] || i * prime[j] > n) break;
v[i * prime[j]] = prime[j];
phi[i * prime[j]] = phi[i] * (i % prime[j] ? prime[j] - 1 : prime[j]);
}
sumPhi[i] = sumPhi[i - 1] + phi[i];//求欧拉函数的前缀和,如果整除分块的话就要用
}
}
int main() {
scanf("%d", &n);
eular(n);
long long ans = 0;
for (int l = 1, r; l <= n; l = r + 1) {//这里是整除分块写法,如果不懂可以直接for 1 to n
r = n / (n / l);
ans += (long long) (sumPhi[r] - sumPhi[l - 1]) * (n / l) * (n / l);
}
printf("%lld\n", ans);
return 0;
}
7.LCMSUM - LCM Sum
题目大意
求
分析
具体做法莫比乌斯反演里写过了,推到最后都会用到
最后可以推出
复杂度
code
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
#define int long long//记得开long long!
using namespace std;
cs int N=1e6;
int ss[N],fi[N+1],f[N+1],cnt;
il int rd(){
ri int x=0,f=1;ri char c=getchar();
while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
return x*f;
}
il void wt(int x){
if(x<0) x=-x,putchar('-');
if(x>=10) wt(x/10);
putchar(x%10+48);
}
il void init(int n){
for(ri int i=2;i<=n;++i){
if(!fi[i]) fi[i]=i-1,ss[++cnt]=i;
for(ri int j=1;j<=cnt&&ss[j]*i<=n;++j){
if(i%ss[j]) fi[i*ss[j]]=fi[i]*fi[ss[j]];
else {fi[i*ss[j]]=fi[i]*ss[j]; break;}
}
}
for(ri int i=2;i<=n;++i){
for(ri int j=i;j<=n;j+=i){
f[j]+=fi[i]*i/2;
}
}
return;
}
signed main(){
int t=rd(),n;init(N);
while(t--) n=rd(),wt(n*(1+f[n])),putchar('\n');
return 0;
}
8.拿行李(极限版) GCD - Extreme (II)
题目大意
求
分析
由于这题有多组数据(还不知道有多少组),所以考虑刷表
把
设
那么有
考虑快速求
复杂度
code
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
#define int long long
using namespace std;
il int rd(){
ri int x=0,f=1;ri char c=getchar();
while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
return x*f;
}
il void wt(int x){
if(x<0) x=-x,putchar('-');
ri short tl=0,a[15]={0};
do{a[++tl]=x%10,x/=10;}while(x);
for(;tl;--tl)putchar(a[tl]+48);
}
cs int N=4e6+1;
int ss[N],cnt,vs[N+5],fi[N+5];
int n,m,as[N+5];
il void init(int n){
as[1]=fi[1]=0;//为方便就令varphi(1)=0了
for(ri int i=2;i<=n;++i){
if(!vs[i]) ss[++cnt]=i,fi[i]=i-1;
for(ri int j=1;j<=cnt&&ss[j]*i<=n;++j){
vs[i*ss[j]]=1;
if(i%ss[j])fi[i*ss[j]]=fi[i]*fi[ss[j]];
else{fi[i*ss[j]]=fi[i]*ss[j];break;}
}
as[i]=fi[i];
}
for(ri int i=2;i*i<=n;++i){
as[i*i]+=fi[i]*i;
for(ri int j=i+1;j*i<=n;++j){
as[j*i]+=fi[i]*j+fi[j]*i;
}
}
for(ri int i=2;i<=n;++i) as[i]+=as[i-1];
}
signed main(){
init(N);
n=rd();
while(n){
wt(as[n]);
putchar('\n');
n=rd();
}
return 0;
}
I went to the woods because I wanted to live deliberately, I wanted to live deep and suck out all the marrow of life, and not when I had come to die, discover that I had not live.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】