费马小定理 & 欧拉定理
Part 1:知识点
费马小定理
若
欧拉定理
若
证明:
设
为 的一个简化剩余系,则 也为 的一个简化剩余系。
所以
约去,可得 。
当
扩展欧拉定理
一道试水模板题【模板】扩展欧拉定理
Part 2:一些习题
1、P4861 按钮
求最小的
由
由欧拉定理知
但
证明: 设
为最小的解,且
那么显然
则有
此时,这与 是最小的解矛盾
所以
那我们可以
#include<bits/stdc++.h>
using namespace std;
int a,m,p,ans;
int gcd(int x,int y)
{
if(y==0)
return x;
return gcd(y,x%y);
}
int get_phi(int x)
{
int res=x;
for(int i=2; i*i<=x; i++)
{
if(x%i==0)
{
res=res/i*(i-1);
while(x%i==0)
x/=i;
}
}
if(x>1)
res=res/x*(x-1);
return res;
}
int ksm(int x,int y)
{
if(y==0)
return 1;
if(y==1)
return x%m;
int tmp=ksm(x,y/2);
if(y%2==0)
return 1LL*tmp*tmp%m;
return 1LL*tmp*tmp%m*x%m;
}
int main()
{
scanf("%d%d",&m,&a);
p=get_phi(m);
ans=p;
if(gcd(a,m)!=1)
{
printf("Let's go Blue Jays!");
return 0;
}
for(int i=1; i*i<=p; i++)
{
if(p%i!=0)
continue;
if(ksm(a,i)==1)
ans=min(ans,i);
else if(ksm(a,p/i)==1)
ans=min(ans,p/i);
}
printf("%d",ans);
return 0;
}
2、P4139 上帝与集合的正确用法
即求
因为幂塔是无穷的,显然大于
显然是个递归的式子。又因为欧拉函数不断地迭代最终必定取到
#include<bits/stdc++.h>
using namespace std;
const int N=10000010;
int T,p,phi[N],v[N],prime[N],tot;
void primes()
{
phi[1]=1;
for(int i=2; i<=1e7; i++)
{
if(!v[i])
{
v[i]=i;
prime[++tot]=i;
phi[i]=i-1;
}
for(int j=1; j<=tot; j++)
{
if(prime[j]>v[i] || prime[j]>1e7/i)
break;
v[i*prime[j]]=prime[j];
if(i%prime[j]==0)
phi[i*prime[j]]=phi[i]*prime[j];
else
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
int pow(int a,int b,int mod)
{
if(b==0)
return 1;
if(b==1)
return a%mod;
int tmp=pow(a,b/2,mod);
if(b%2==0)
return 1LL*tmp*tmp%mod;
return 1LL*tmp*tmp%mod*a%mod;
}
int calc(int n)
{
if(n==1)
return 0;
return pow(2,calc(phi[n])+phi[n],n);
}
int main()
{
primes();
scanf("%d",&T);
while(T--)
{
scanf("%d",&p);
printf("%d\n",calc(p));
}
return 0;
}
3、P3934 [Ynoi2016] 炸脖龙 I
我愿称之为相逢是问候的前置题
操作
根据第2题的结论,幂塔不断迭代最后为定值,而迭代次数为使得
因此我们考虑像上一题一样暴力递归即可。但注意,此时指数不一定大于等于
代码实现细节多,写在代码注释中
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=500010,M=20000010;
int n,m,phi[M],prime[M],tot,v[M];
bool flag;
LL c[N];
void prework() //预处理1~2e7所有的phi值
{
phi[1]=1;
for(int i=2; i<=2e7; i++)
{
if(!v[i])
{
v[i]=i;
prime[++tot]=i;
phi[i]=i-1;
}
for(int j=1; j<=tot; j++)
{
if(prime[j]>v[i] || prime[j]>2e7/i)
break;
v[i*prime[j]]=prime[j];
if(i%prime[j]==0)
phi[i*prime[j]]=prime[j]*phi[i];
else
phi[i*prime[j]]=(prime[j]-1)*phi[i];
}
}
}
void add(int x,int y)
{
for(x; x<=n+1; x+=(x&-x))
c[x]+=(LL)y;
}
LL ask(int x)
{
LL res=0;
for(x; x; x-=(x&-x))
res+=c[x];
return res;
}
LL ksm(LL x,LL y,LL mod)
{
flag=0;
LL res=1;
if(x>=mod) //大坑点!!!!
x%=mod,flag=1;
while(y)
{
if(y&1)
res*=x;
x*=x;
if(res>=mod) //判断两次
res%=mod,flag=1;
if(x>=mod)
x%=mod,flag=1;
y>>=1;
}
return res;
}
LL dfs(int now,LL p,int k)
{
flag=0;
LL cur=ask(now);
if(p==1 || now==k)
{
if(cur>=p) //注意要判断
flag=1,cur%=p;
return cur;
}
LL tur=dfs(now+1,phi[p],k);
return ksm(cur,flag? tur+(LL)phi[p]:tur,p); //注意指数选择的情况
}
int main()
{
prework();
scanf("%d%d",&n,&m);
for(int i=1,last=0; i<=n; i++)
{
int x;
scanf("%d",&x);
add(i,x-last); //差分数组
last=x;
}
for(int i=1; i<=m; i++)
{
int op,l,r,x,p;
scanf("%d%d%d",&op,&l,&r);
if(op==1)
{
scanf("%d",&x);
add(l,x); add(r+1,-x);
}
else
{
scanf("%d",&p);
printf("%lld\n",dfs(l,(LL)p,r));
}
}
return 0;
}
4、P3747 [六省联考 2017] 相逢是问候
一道线段树+扩展欧拉定理的毒瘤题
操作
根据第2题的结论,
因此,类似上帝造题的7分钟2,我们对线段树的每个叶子节点记录一个迭代次数
注意到这题的指数不一定大于
时间复杂度为
#include<bits/stdc++.h>
#define LL long long
#define lc(p) p*2
#define rc(p) p*2+1
using namespace std;
const int N=50010,M=50,MAXN=10010;
struct SegmentTree
{
LL sum,t;
#define sum(x) tree[x].sum
#define t(x) tree[x].t
}tree[4*N];
int n,m,phi[M],tphi;
LL P,c,a[4*N],pow1[MAXN][M],pow2[MAXN][M];
bool b1[MAXN][M],b2[MAXN][M],flag;
int get_phi(int x)
{
int res=x;
for(int i=2; i*i<=x; i++)
{
if(x%i==0)
{
res=res/i*(i-1);
while(x%i==0)
x/=i;
}
}
if(x>1)
res=res/x*(x-1);
return res;
}
void prework()
{
LL tmp=P;
phi[0]=P; //预处理有用的phi
while(tmp!=1)
tmp=get_phi(tmp),phi[++tphi]=tmp;
phi[++tphi]=1;
for(int i=0; i<=tphi; i++) //光速幂
{
pow1[0][i]=1;
for(int j=1; j<=10000; j++)
{
pow1[j][i]=pow1[j-1][i]*c;
if(pow1[j][i]>=phi[i])
pow1[j][i]%=phi[i],b1[j][i]=1;
b1[j][i]|=b1[j-1][i]; //判断是扩展欧拉定理的哪种情况
}
}
for(int i=0; i<=tphi; i++)
{
pow2[0][i]=1;
b2[1][i]=b1[10000][i];
for(int j=1; j<=10000; j++)
{
pow2[j][i]=pow2[j-1][i]*pow1[10000][i];
if(pow2[j][i]>=phi[i])
pow2[j][i]%=phi[i],b2[j][i]=1;
b2[j][i]|=b2[j-1][i];
}
}
}
LL calc(LL x,int k) //O(1)回答
{
flag=0;
LL x1=x%10000,x2=x/10000;
LL res=pow1[x1][k]*pow2[x2][k];
if(res>=phi[k])
res%=phi[k],flag=1;
flag|=b1[x1][k]|b2[x2][k];
return res;
}
LL dfs(LL cur,int dep,int k) //递归log_p层
{
flag=0;
if(dep==k)
{
if(cur>=phi[dep])
flag=1,cur%=phi[dep];
return cur;
}
LL tur=dfs(cur,dep+1,k);
return calc(flag? tur+phi[dep+1]:tur,dep);
}
void pushup(int p)
{
t(p)=min(t(lc(p)),t(rc(p)));
sum(p)=(sum(lc(p))+sum(rc(p)))%P;
}
void build(int p,int l,int r)
{
if(l==r)
{
scanf("%d",&a[p]);
sum(p)=a[p];
return;
}
int mid=(l+r)>>1;
build(lc(p),l,mid);
build(rc(p),mid+1,r);
pushup(p);
}
void change(int p,int l,int r,int ql,int qr)
{
if(t(p)>=tphi) //没有必要继续修改
return;
if(l==r)
{
t(p)++;
sum(p)=dfs(a[p],0,t(p));
return;
}
int mid=(l+r)>>1;
if(ql<=mid && t(lc(p))<tphi)
change(lc(p),l,mid,ql,qr);
if(qr>mid && t(rc(p))<tphi)
change(rc(p),mid+1,r,ql,qr);
pushup(p);
}
LL ask(int p,int l,int r,int ql,int qr)
{
if(ql<=l && qr>=r)
return sum(p);
int mid=(l+r)>>1;
LL val=0;
if(ql<=mid)
val+=ask(lc(p),l,mid,ql,qr);
if(qr>mid)
val+=ask(rc(p),mid+1,r,ql,qr);
return val%P;
}
int main()
{
scanf("%d%d%lld%lld",&n,&m,&P,&c);
prework();
build(1,1,n);
for(int i=1; i<=m; i++)
{
int op,l,r;
scanf("%d%d%d",&op,&l,&r);
if(op==0)
change(1,1,n,l,r);
else
printf("%lld\n",ask(1,1,n,l,r));
}
return 0;
}
**
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效