欧拉定理

参考博客

欧拉定理:

若正整数 a,n 互质,则 aφ(p)1(modp)

推论(扩展欧拉定理):

ab{ab mod φ(p)gcd(a,p)=1abgcd(a,p)1,b<φ(p)     (modp)ab mod φ(p)+φ(p)gcd(a,p)1,bφ(p)

证明: abab mod φ(p)  gcd(a,p)=1

b=q×φ(p)+rabaq×φ(p)+r(aφ(p))q×ar1q×ararab mod φ(p)

若正整数 a,n 互质,则满足 ax1(modn) 的最小正整数 x0φ(n) 的约数。

假设满足 ax1(modn) 的最小正整数 x0 不是 φ(n) 的约数。

φ(n)=qx0+r(0<r<x0)

ax01(modn)

aqx01(modn)

aφ(n)1(modn)

ar1(modn)

r<x0 ,这与 x0 最小矛盾,所以假设不成立。

例:abmodm

代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e7+10;
int a, m;
char b[maxn];

int phi(int x) {
	int res = x;
	int cnt = sqrt(x);
	for(int i=2; i<=cnt; ++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;
}

LL quick_pow(LL x, LL y) { 
	LL res = 1;
	while(y) {
		if(y&1)
			res = (res*x)% m;
		x = (x*x) % m;
		y >>= 1;
	}
	return res % m;
}

int main () {
	scanf("%d%d%s", &a, &m, b);
	int Phi = phi(m); // m 的欧拉函数值 
	int rmd = 0;
	int len = strlen(b);
	int flag = 1;
	for(int i=0; i<len; ++i) { // 将 b 对 Phi 取模 
		rmd = rmd * 10;
		if(rmd >= Phi) flag = 0;
		rmd = rmd % Phi;
		rmd = rmd + (b[i]-'0');
		if(rmd >= Phi) flag = 0;
		rmd = rmd % Phi;
	}
	if(flag) // 当 b < Phi 
		printf("%lld\n", quick_pow(a, rmd));
	else printf("%lld\n", quick_pow(a, rmd+Phi));
	return 0;
}

 

相逢是问候

这是一道非常简单且有意思的题:
有两种操作:
1. i[l,r],ai=cai
2. 求i=1nai

对于此题的修改操作,即是求 c ^ c ^ ^ ai (modp) (此处“^”表示乘方)可以证明当层数足够多时其结果为常数。
所以可以使用势能线段树进行修改,对于每个点的修改利用扩展欧拉定理求解,并统计其修改次数,当其足够多时便不用修改。
但是此时 3log 的复杂度还不足以通过此题,可以预处理快速幂从而省去一个 log 的复杂度即可。

代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;

inline ll read(){
	ll s=0,k=1;
	char c=getchar();
	while(c>'9'||c<'0'){
		if(c=='-')k=-k;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		s=(s<<3)+(s<<1)+(c^48);
		c=getchar();
	}
	return s*k;
} 

const int N=1e5+10,M=55; 
int n,m,cnt;
ll p,c,a[N],phi[M],op,x,y,Mul1[N][M],Mul2[N][M];
bool fl,f1[N][M],f2[N][M];
struct node{
	ll v,mn;
}t[N<<1];

inline ll Phi(ll V){
	ll res=V,nq=sqrt(V);
	for(register ll i=2;i<=nq;i++){
		if(V%i==0){
			res=res/i*(i-1);
			while(V%i==0) V/=i;
		}
	}
	if(V>1) res=res/V*(V-1);
	return res;
}

inline void init(){
    ll tmp=p; phi[0]=p;
    while(tmp!=1){
    	tmp=Phi(tmp);
		phi[++cnt]=tmp;
	} 
    phi[++cnt]=1;
    for(register int i=0;i<=cnt;i++){ //预处理快速幂
    	Mul1[0][i]=1;Mul2[0][i]=1;
    	for(register int j=1;j<=10000;j++){
    		Mul1[j][i]=Mul1[j-1][i]*c;
    		if(Mul1[j][i]>=phi[i]) 
			Mul1[j][i]%=phi[i],f1[j][i]=1;
    		f1[j][i]|=f1[j-1][i];
		}
		f2[1][i]=f1[10000][i];
        for(register int j=1;j<=10000;++j){
            Mul2[j][i]=Mul2[j-1][i]*Mul1[10000][i];
            if(Mul2[j][i]>=phi[i]) 
			Mul2[j][i]%=phi[i],f2[j][i]=1;
            f2[j][i]|=f2[j-1][i];
        }
	}
}

inline ll ksm(ll t,ll Mod){
	ll mod=phi[Mod];
    ll res,v1=t%10000,v2=t/10000;
    fl=0;
    res=Mul1[v1][Mod]*Mul2[v2][Mod];
    if(res>=mod) res%=mod,fl=1;
    else fl=fl|f1[v1][Mod]|f2[v2][Mod];
    return res;
}

inline ll dfs(ll V,int deep,int pH){ // 运用扩展欧拉定理求答案 c^c^c...^c^ai
    if(deep==pH){
        if(V>=phi[deep]) fl=1,V%=phi[deep];
        return V;
    }
    ll B=dfs(V,deep+1,pH);
    if(fl) return ksm(B+phi[deep+1],deep);
    return ksm(B,deep);
}	

inline void pushup(int s){   // 势能线段树(执行足够多次操作后将会是一个固定值)
	t[s].v=(t[s<<1].v+t[s<<1|1].v);
	if(t[s].v>=p) t[s].v-=p;
	t[s].mn=min(t[s<<1].mn,t[s<<1|1].mn);
}

inline void build(int s,int l,int r){
    if(l==r){
    	t[s].v=a[l];
    	return;
	}
	int mid=(l+r)>>1;
	build(s<<1,l,mid);
	build(s<<1|1,mid+1,r);
	pushup(s);
}

inline void update(int L,int R,int l,int r,int s){
    if(t[s].mn>=cnt) return ;
    if(L==R){
        ++t[s].mn; 
        fl=0;
		t[s].v=dfs(a[L],0,t[s].mn);
        return ;
    }
    int mid=(L+R)>>1;
    if((l<=mid)&&(t[s<<1].mn<cnt)) update(L,mid,l,r,s<<1);
    if((r>mid)&&(t[s<<1|1].mn<cnt)) update(mid+1,R,l,r,s<<1|1);
    pushup(s);
}

inline ll query(int L,int R,int l,int r,int s){
    if((l<=L)&&(R<=r)) return t[s].v;
    int mid=(L+R)>>1;
	ll res=0;
    if(l<=mid) res+=query(L,mid,l,r,s<<1);
    if(res>=p) res-=p;
    if(r>mid) res+=query(mid+1,R,l,r,s<<1|1);
    if(res>=p) res-=p;
    return res;	
}

int main() {
	n=read();m=read();p=read();c=read();
	for(register int i=1;i<=n;i++) a[i]=read();
    build(1,1,n); 
	init();
    while(m--){
        int op,x,y;
        op=read();x=read();y=read();
        if(op==0) update(1,n,x,y,1);
        else printf("%lld\n",query(1,n,x,y,1));
	}
	return 0;
}
posted @   programmingysx  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· [翻译] 为什么 Tracebit 用 C# 开发
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· DeepSeek崛起:程序员“饭碗”被抢,还是职业进化新起点?
· 2分钟学会 DeepSeek API,竟然比官方更好用!
· .NET 使用 DeepSeek R1 开发智能 AI 客户端
点击右上角即可分享
微信分享提示