[Codeforces]906D Power Tower


  雖說是一道裸題,但還是讓小C學到了一點姿勢的。

Description

  給定一個長度為n的數組w,模數m和詢問次數q,每次詢問給定l,r,求:

    

  對m取模的值。

Input

  第一行兩個整數n,m,表示數組長度和模數。
  接下來一行n個數,表示w數組。
  接下來一行一個整數q,表示詢問次數。
  接下來q行,每行兩個整數l,r,表示一次詢問。

Output

  對於每次詢問,輸出一行一個整數表示答案。

Sample Input

  6 1000000000
  1 2 2 3 3 3
  8
  1 1
  1 6
  2 2
  2 3
  2 4
  4 4
  4 5
  4 6

Sample Output

  1
  1
  2
  4
  256
  3
  27
  597484987

HINT

  1 ≤ n ≤ 105,1 ≤ m ≤ 1091 ≤ wi ≤ 109,1 ≤ q ≤ 105,1 ≤ l ≤ r ≤ n

 

Solution

  看到這么清奇的式子,你大概會第一時間想到降冪大法吧?

  先說說擴展歐拉定理,對於任意正整數a,b,p:

    

  所以假設堆疊的冪次足夠大,那么式子就可以轉化為:

      

  

  已知p經過至多2log次phi就會變成1。

  所以遞歸求解,至多走到2log層模數就會變成1,所以返回0就行。

  所以這道題就非常顯然了,首先預處理出m的所有phi,對於每個詢問,從l開始直接遞歸暴力,直到模數為1時返回。

  還有一個問題,在求a^b%p的時候,怎么比較b和phi(p)的大小呢?

  一種思路就是暴力計算a的后log項的值,注意還要特判1的情況,但這樣寫起來確實麻煩。

  當然,有一種非常精妙的取模寫法:

int modulo(ll x,int mod) {return x<mod?x:x%mod+mod;}

  這是在做什么呢?這就是在比較b和phi(p)的大小,如果b<phi(p),返回b;否則返回b%phi(p)+phi(p)。

  然后原式就變成了這樣:

    

  這樣做看上去漏洞百出,可能的情況是,原本我們要計算,其中大等於

  然而我們計算,將取模后,卻發現小於了。

  是否有這種可能呢?

  其實就相當於判斷是否有可能成立,我們可以發現,當a>2時式子是不可能成立的。

  所以我們來看一看 是否有可能成立。

  有可能。

  當且僅當p=6時,不等式成立。 

  然而6有什么特殊的性質呢?

  我們發現phi(x)=6只有三個解:x=7,x=9或x=18。

  所以接下來我們只要證明   和   即  和  在對x取模的意義下相等即可。(其中phi(x)=6)

  若a為x的倍數,顯然它們對x取模都等於0,對於答案無影響。

  當x=7時,,所以 

  當x=9時,若 ,則影響同上;

        若 ,一定有,所以一定有 

               所以一定有 ,對於答案是沒有影響的;

  當x=18時,若  或 ,則影響同上;

       我們有一個顯然的結論:同余方程  的解為 

       若 ,則 ,則 ,則 

       若 ,則  且 ,則 ,則 

  所以綜上,我們就證明了該算法的正確性。

  時間復雜度

#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define MN 100005
using namespace std;
int a[MN],mod[MN];
int n,p;
bool fg;

inline int read()
{
    int n=0,f=1; char c=getchar();
    while (c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
    while (c>='0' && c<='9') {n=n*10+c-'0'; c=getchar();}
    return n*f;
}

inline int pro(ll x,int md) {return x<md?x:x%md+md;}
inline int mi(int x,int y,int md)
{
    register int z=1;
    for (;y;x=pro(1LL*x*x,md),y>>=1)
        if (y&1) z=pro(1LL*z*x,md);
    return z;
}

int dfs(int x,int y,int lim)
{
    if (x==lim) return a[x]>=mod[y]?a[x]%mod[y]+mod[y]:a[x];
    if (mod[y]==1) return 1;
    return mi(a[x],dfs(x+1,y+1,lim),mod[y]);
}

int main()
{
    register int i,j,x,y;
    n=read(); mod[1]=read();
    for (i=1;mod[i]>1;++i)
    {
        mod[i+1]=x=mod[i];
        for (j=2;j*j<=x;++j)
        {
            for (fg=0;x%j==0;x/=j,fg=true);
            if (fg) mod[i+1]=1LL*mod[i+1]*(j-1)/j;
        }
        if (x>1) mod[i+1]=1LL*mod[i+1]*(x-1)/x;
    }
    for (i=1;i<=n;++i) a[i]=read();
    for (p=read();p;--p)
    {
        x=read(); y=read();
        printf("%d\n",dfs(x,1,y)%mod[1]);
    }
}

Last Word

  打Codeforces的時候正納悶這種情況該怎么處理,卻發現大佬們清一色都是這么寫的。

  小C覺得自己的證明蠢得不行啊……

  如果讀者有更直觀的證明該算法的正確性的方法請務必告訴小C。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM