尤拉定理:
若正整數 \(a,n\) 互質,則 \(a^{\varphi(p)}\equiv1(\bmod p)\)
推論(擴充套件尤拉定理):
證明: \(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;
}