1. 程式人生 > >【CodeForces】906 D. Power Tower 擴展歐拉定理

【CodeForces】906 D. Power Tower 擴展歐拉定理

ces etc targe 整數 force cau getchar() main digi

【題目】D. Power Tower

【題意】給定長度為n的正整數序列和模數m,q次詢問區間[l,r]累乘冪%m的答案。n,q<=10^5,m,ai<=10^9。

【算法】擴展歐拉定理

【題解】擴展歐拉定理的形式:

$$a^b\equiv a^{b\%\varphi(p)+\varphi(p)} \ \ mod \ \ p \ \ (b\leq \varphi(p))$$

特別註意當b<φ(p)且(a,p)≠1時不變

假如現在是三個累乘冪a^(b^c),那麽根據擴展歐拉定理:

$$a^{b^c}\ \ mod \ \ p\equiv a^{b^c\%\varphi(p)+\varphi(p)} \ \ mod \ \ p$$

這樣我們只需要計算:

$$b^c\ \ mod \ \ \varphi(p)$$

更多個累乘冪的時候只需要不斷遞歸取φ,直至1為止(φ(1)=1)。可以證明至多log(p)次可以得到答案。

這樣計算累乘冪的復雜度就是O(log p*log n),也即一次詢問的極限復雜度。

這裏過程中用到的歐拉函數至多log p個,直接暴力求解,預處理復雜度O(log p*√n),用map存儲,實現中可以直接記憶化。

總復雜度O(q*log p*log n)。

技術分享圖片
#include<cstdio>
#include<map>
#define ll long long
bool isdigit(char
c){return c>=0&&c<=9;} int read(){ int s=0,t=1;char c; while(!isdigit(c=getchar()))if(c==-)t=-1; do{s=s*10+c-0;}while(isdigit(c=getchar())); return s*t; } using namespace std; const int maxn=100010; map<int,int>p; int a[maxn],n,m,q; int phi(int n){ if(p.count(n))return
p[n]; int ans=n,m=n; for(int i=2;i*i<=n;i++)if(n!=1&&n%i==0){ ans=ans/i*(i-1); while(n%i==0)n/=i; } if(n>1)ans=ans/n*(n-1); p[m]=ans; return ans; } int mod(ll x,int y){return x<y?x:x%y+y;}//focus on add,because 2e9*2>int int power(int x,int k,int m){ int ans=1; while(k){ if(k&1)ans=mod(1ll*ans*x,m); x=mod(1ll*x*x,m); k>>=1; } return ans; } int calc(int l,int r,int m){ if(l==r||m==1)return mod(a[l],m); return power(a[l],calc(l+1,r,phi(m)),m); } int main(){ n=read();m=read(); for(int i=1;i<=n;i++)a[i]=read(); q=read(); while(q--){ int l=read(),r=read(); printf("%d\n",calc(l,r,m)%m); } return 0; }
View Code

實現:

1.遞歸過程中直接返回ans+mod,這樣下一層就自帶+φ(m)了,最後輸出答案記得%m就可以了。

2.快速冪過程中的取模改為 int mod(ll x,int y){return x<y?x:x%y+y;} ,這樣到某一次數字超過y之後,後面每次都會強制超過y然後+y直至最後一次。

【CodeForces】906 D. Power Tower 擴展歐拉定理