尤拉定理

programmingysx發表於2024-06-29

尤拉定理:

若正整數 \(a,n\) 互質,則 \(a^{\varphi(p)}\equiv1(\bmod p)\)

推論(擴充套件尤拉定理):

\[a^b\equiv\begin{cases} a^{b\ \bmod\ \varphi(p)}\ \ \ \ \ \ \ \ \ \gcd(a,p)=1\\ a^b\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \gcd(a,p)!=1,b<\varphi(p)\ \ \ \ \ (\bmod p)\\ a^{b\ \bmod\ \varphi(p)+\varphi(p)}\ \gcd(a,p)!=1,b\geq\varphi(p) \end{cases} \]

證明: \(a^b\equiv a^{b\ \bmod\ \varphi(p)}\ \ \gcd(a,p)=1\)

\[設 b=q\times \varphi(p)+r\\ a^b\equiv a^{q\times\varphi(p)+r}\equiv (a^{\varphi(p)})^q\times a^r\equiv 1^q\times a^r\equiv a^r\equiv a^{b\ \bmod\ \varphi(p)} \]

若正整數 \(a,n\) 互質,則滿足 \(a^x\equiv 1(\bmod n)\) 的最小正整數 \(x_0\)\(\varphi(n)\) 的約數。

假設滿足 \(a^x\equiv 1(\bmod n)\) 的最小正整數 \(x_0\) 不是 \(\varphi(n)\) 的約數。

\(\varphi(n)=qx_0+r(0<r<x_0)\)

\(\therefore a^{x_0}\equiv 1(\bmod n)\)

\(\therefore a^{qx_0}\equiv 1(\bmod n)\)

\(\because a^{\varphi(n)}\equiv 1(\bmod n)\)

\(\therefore a^r\equiv 1(\bmod n)\)

\(r<x_0\) ,這與 \(x_0\) 最小矛盾,所以假設不成立。

例:\(a^b\bmod m\)

程式碼
#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\). \(\forall i\in[l,r],a_i=c^{a_i}\)
\(2\). 求\(\sum_{i=1}^na_i\)

對於此題的修改操作,即是求 \(c\) ^ \(c\) ^ \(\cdots\) ^ \(a_i\) \((\bmod p)\) (此處“^”表示乘方)可以證明當層數足夠多時其結果為常數。
所以可以使用勢能線段樹進行修改,對於每個點的修改利用擴充套件尤拉定理求解,並統計其修改次數,當其足夠多時便不用修改。
但是此時 \(3\log\) 的複雜度還不足以透過此題,可以預處理快速冪從而省去一個 \(\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;
}