1. 程式人生 > >【bzoj4869】相逢是問候

【bzoj4869】相逢是問候

可能 lse IV name 忘記 葉子節點 gin phi port

Portal-->bzoj4869

Solution

  這道題的話。。長得就是線段樹的樣子qwq

  如果做過的話。。可能會聯想到bzoj3211(沒寫博qwq晚點再說吧哈哈。。)

  首先大膽猜一波結論:這題跟3211一樣也是修改到了一定程度就不會再有變化了!那然後寫起來就是線段樹暴力修改然後如果整個區間達到了修改上限的話那就不走了

  然而這個上限是啥呢。。

?   

  這裏還是要用到擴展歐拉定理
\[ a^b\equiv \begin{cases} &a^{b\%\varphi(p)} &\gcd(a,p)=1\&a^b &\gcd(a,p)\neq1,b<\phi(p)\&a^{b\%\varphi(p)+\varphi(p)} &\gcd(a,p)\neq1,b\geq\phi(p) \end{cases}\pmod p \]


(然而實際上在這題裏面第一條並沒有什麽用畢竟gcd(a,p)=1?這個限制有點令人難受qwq)

  然後接著我們來看一下這個修改操作

  經過多次修改之後應該是長成一堆\(c\)指數翻上去,然後最後那個指數是\(c^a_i\)這個樣子(有沒有長得很像上帝和集合(bzoj3884)那道題的那一堆\(2\)?)

  然後我們就用類似bzoj3884中的處理方法,大力降冪,跟那題一樣,當\(\varphi(p)\)降到\(1\)了之後\(b%\varphi(1)+\varphi(1)=1\),也就是說接下來無論再怎麽搞都不會再變了(每個數最多被修改\(log\)次就不會再變了,具體為啥的話在bzoj3884裏面有寫就不提了)

  與那題不同的是,這裏需要判斷指數與模數的大小關系來判斷能否降成\(b\% \varphi(p)+\varphi(p)\)

?   

  然而這題寫起來其實。。有點惡心

  具體一點的話就是:

  首先我們預處理出\(p\)被取\(\varphi\)多少次之後會變成\(1\),記這個次數為\(Mx\),並且將中間每一步的結果記錄在一個數組裏面,方便我們後面求值的時候直接調用作為模數

  然後我們開一棵線段樹,對於修改操作,我們在每個區間記錄一個\(sum[x]\)和一個\(mn[x]\),前者表示答案,後者表示這個區間中被修改最少的那個位置被修改了多少次

  在修改的時候,如果說當前區間的\(mn[x]>Mx\)

那麽說明這個區間所有的位置都已經達到上限可以直接跳過不用管了,否則遞歸進去處理,一直到葉子節點。葉子節點的話,每次修改的時候能直接改的只是這個節點的\(mn[x]\),而單點的\(sum[x]\)則要重新再算一次

  至於怎麽計算\(sum[x]\)的話,可以考慮從降冪的最後一層層推回來,然而因為每次我們推出來的都是下一層的指數部分,如果用最暴力的快速冪求解的話,加上層數最多是\(log\)以及線段樹的一個\(log\),總的復雜度會變成\(O(nlog^3n)\),在TLE的邊緣試探。。

?  

  然後我們註意到每次快速冪底數其實都是\(c\),所以我們可以考慮將\(c\)的一些次方預處理出來,這樣就可以直接調用了

  然而這裏有一個小問題,次方數。。最大可以去到\(10^8\),所以這裏要用一個小trick:

  我們預處理出\(c\)\(1-10000\)次方以及\(c^{10000}\)\(1-10000\)次方,這樣對於一個指數,大於\(10^4\)的部分和小於\(10^4\)的部分分開來調用就好了,然後就可以十分愉快地省掉一個\(log\)\(O(nlog^2n)\)相對來說就優秀很多啦

  

?  此外還有一個比較麻煩的地方是,我們需要判斷指數和\(p\)的大小,註意,這裏的指數是不能取模的,那就有點糟糕了qwq

  這裏的解決方案是,註意到由於我們是倒過來推的,這裏的指數在用擴展歐拉定理降冪之後會變成\(c\)的若幹次方,所以我們可以先預處理出\(c\)的若幹次方的值(處理到這個值\(>10^9\)就可以了,也可以更加小一點,反正\(>10^8\)就行),對於\(>10^8\)的部分我們直接打上一個\(tag\)(代碼裏面我是直接將表示當前冪指數的那個變量賦成\(-1\)),然後根據是否有\(tag\)判斷是否能夠用擴展歐拉定理的第三條就好了

  

  細節什麽的比較。。嗯qwq(調試調了不知道多久最後終於調出來了發現是判斷的時候下標忘記+1也是很開心。。。)

?  代碼大概長這個樣子

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int MAXN=50010,SEG=MAXN*4;
int mod[MAXN],prime[MAXN];
int pwc[10010][30],pwc1[10010][30],pc[60];
bool vis[MAXN];
int n,m,P,c,Mx,Up=10000,ans;
int a[MAXN];
namespace Seg{/*{{{*/
    int ch[SEG][2],sum[SEG],mi[SEG];
    int n,tot;
    void pushup(int x){
        sum[x]=(1LL*sum[ch[x][0]]+sum[ch[x][1]])%P;
        mi[x]=min(mi[ch[x][0]],mi[ch[x][1]]);
    }
    void _build(int x,int l,int r){
        sum[x]=0; mi[x]=0;
        if (l==r) {sum[x]=a[l];return;}
        int mid=l+r>>1;
        ch[x][0]=++tot; _build(ch[x][0],l,mid);
        ch[x][1]=++tot; _build(ch[x][1],mid+1,r);
        pushup(x);
    }
    void build(int _n){n=_n;tot=1;_build(1,1,n);}
    int powc(int Pw,int Mod){return 1LL*pwc1[Pw/Up][Mod]*pwc[Pw%Up][Mod]%mod[Mod];}
    int calc(int loc,int Pw){
        int nowmi=a[loc],ret=a[loc];
        for (int i=Pw-1;i>=0;--i){
            if (nowmi==-1||nowmi>=mod[i+1]){
                ret=powc(ret%mod[i+1]+mod[i+1],i);
                nowmi=-1;
            }
            else{
                ret=powc(ret,i);
                if (nowmi<60) nowmi=pc[nowmi];
                else nowmi=-1;
            }
        }
        return ret;
    }
    void _update(int x,int l,int r,int lx,int rx){
        if (mi[x]>=Mx) return;
        if (lx==rx){
            ++mi[x];
            sum[x]=calc(lx,mi[x]);
            return;
        }
        int mid=lx+rx>>1;
        if (l<=mid) _update(ch[x][0],l,r,lx,mid);
        if (r>mid) _update(ch[x][1],l,r,mid+1,rx);
        pushup(x);
    }
    void update(int l,int r){_update(1,l,r,1,n);}
    int _query(int x,int l,int r,int lx,int rx){
        if (l<=lx&&rx<=r) return sum[x];
        int mid=lx+rx>>1,ret=0;
        if (l<=mid) ret=(1LL*ret+_query(ch[x][0],l,r,lx,mid))%P;
        if (r>mid) ret=(1LL*ret+_query(ch[x][1],l,r,mid+1,rx))%P;
        return ret;
    }
    int query(int l,int r){return _query(1,l,r,1,n);}
}/*}}}*/
void prework(int n);
int phi(int x);
int ksm(int x,int y,int p);
 
int main(){
#ifndef ONLINE_JUDGE
    freopen("a.in","r",stdin);
#endif
    int op,l,r;
    scanf("%d%d%d%d",&n,&m,&P,&c);
    for (int i=1;i<=n;++i) scanf("%d",a+i);
    prework(MAXN);
    Seg::build(n);
    for (int i=1;i<=m;++i){
        scanf("%d%d%d",&op,&l,&r);
        if (op==0)
            Seg::update(l,r);
        else{
            ans=Seg::query(l,r);
            printf("%d\n",ans);
        }
    }
}
 
void prework(int n){
    int cnt=0;
    for (int i=2;i<n;++i){
        if (!vis[i]) 
            prime[++cnt]=i;
        for (int j=1;j<=cnt&&prime[j]*i<n;++j){
            vis[i*prime[j]]=1;
            if (i%prime[j]==0) break;
        }
    }
    mod[0]=P;Mx=0;
    while (mod[Mx]!=1) ++Mx,mod[Mx]=phi(mod[Mx-1]);
    mod[++Mx]=1;
     
    for (int i=0;i<=Up;++i)
        for (int j=0;j<=Mx;++j)
            pwc[i][j]=ksm(c,i,mod[j]);
    for (int i=0;i<=Up;++i)
        for (int j=0;j<=Mx;++j)
            pwc1[i][j]=ksm(ksm(c,Up,mod[j]),i,mod[j]);
    //...?
    pc[0]=1;
    int flag=100000;
    for (int i=1;i<60;++i){
        if (pc[i-1]==-1) pc[i]=-1;
        if (1LL*pc[i-1]*c<=1000000000)
            pc[i]=pc[i-1]*c;
        else
            pc[i]=-1,flag=min(flag,i);
    }
}
 
int phi(int x){
    int ret=x;
    for (int i=1;prime[i]*prime[i]<=x;++i){
        if (x%prime[i]) continue;
        ret=ret-ret/prime[i];
        while (x%prime[i]==0) x/=prime[i];
    }
    if (x>1) ret=ret-ret/x;
    return ret;
}
 
int ksm(int x,int y,int p){
    int ret=1,base=x;
    for (;y;y>>=1,base=1LL*base*base%p)
        if (y&1) ret=1LL*ret*base%p;
    return ret;
}

【bzoj4869】相逢是問候