1. 程式人生 > >[六省聯考2017]相逢是問候(線段樹+拓展尤拉定理)

[六省聯考2017]相逢是問候(線段樹+拓展尤拉定理)

好題啊!

調了一箇中午,發現有一條語句 \(RE\) 了。在 \(windows\) 下沒關係,\(linux\) 下有問題,大大的問題。

while(phi[tot]!=1) phi[++tot]=calc_phi(phi[tot-1]);

算是拓展尤拉定理的題吧。線段樹只是一個工具,最主要還是暴力修改。因為 \(\varphi\) 不斷套下去最多會有 \(\lfloor \log n\rfloor\) 層,所以我們對於每一層暴力算一遍,加上快速冪,時間複雜度 \(O(n\log^3 n)\),顯然可能被卡。怎麼優化呢?

\(O(\log n)\) 的快速冪換成 \(O(1)\)

的快速冪就好了,時間複雜度 \(O(n\log^2 n)\)

\(Code\ Below:\)

#include <bits/stdc++.h>
#define int long long
#define lson (rt<<1)
#define rson (rt<<1|1)
using namespace std;
const int maxn=100000+10;
const int base=(1<<14)-1;
int n,m,p,c,a[maxn],sum[maxn<<2],Min[maxn<<2],pw1[55][maxn],pw2[55][maxn],phi[maxn],tot;
bool b1[55][maxn],b2[55][maxn],flag;

inline int read(){
    register int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return (f==1)?x:-x;
}

int calc_phi(int n){
    int ans=n,m=sqrt(n);
    for(int i=2;i<=m;i++){
        if(n%i==0){
            ans=ans/i*(i-1);
            while(n%i==0) n/=i;
        }
    }
    if(n>1) ans=ans/n*(n-1);
    return ans;
}

void pre(){
    int tmp=p;phi[0]=p;
    while(tmp!=1) tmp=calc_phi(tmp),phi[++tot]=tmp;
    phi[++tot]=1;
    for(int i=0;i<=tot;i++){
        pw1[i][0]=1;
        for(int j=1;j<=base+1;j++){
            pw1[i][j]=pw1[i][j-1]*c;
            if(pw1[i][j]>=phi[i]) b1[i][j]=1,pw1[i][j]%=phi[i];
            b1[i][j]|=b1[i][j-1];
        }
    }
    for(int i=0;i<=tot;i++){
        pw2[i][0]=1;
        b2[i][1]=b1[i][base+1];
        for(int j=1;j<=base;j++){
            pw2[i][j]=pw2[i][j-1]*pw1[i][base+1];
            if(pw2[i][j]>=phi[i]) b2[i][j]=1,pw2[i][j]%=phi[i];
            b2[i][j]|=b2[i][j-1];
        }
    }
}

int calc(int a,int dep){
    flag=0;
    int x=a&base,y=(a>>14)&base;
    int ans=pw1[dep][x]*pw2[dep][y];
    if(ans>=phi[dep]) flag=1,ans%=phi[dep];
    flag|=b1[dep][x]|b2[dep][y];
    return ans;
}

int dfs(int a,int dep,int lim){
    flag=0;
    if(dep==lim){
        if(a>=phi[dep]) flag=1,a%=phi[dep];
        return a;
    }
    int b=dfs(a,dep+1,lim);
    return calc(flag?b+phi[dep+1]:b,dep);
}

inline void pushup(int rt){
    sum[rt]=(sum[lson]+sum[rson])%p;
    Min[rt]=min(Min[lson],Min[rson]);
}

void build(int l,int r,int rt){
    if(l == r){
        sum[rt]=a[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,lson);
    build(mid+1,r,rson);
    pushup(rt);
}

void update(int L,int R,int l,int r,int rt){
    if(Min[rt]>=tot) return ;
    if(l == r){
        Min[rt]++;
        sum[rt]=dfs(a[l],0,Min[rt]);
        return ;
    }
    int mid=(l+r)>>1;
    if(L <= mid) update(L,R,l,mid,lson);
    if(R > mid) update(L,R,mid+1,r,rson);
    pushup(rt);
}

int query(int L,int R,int l,int r,int rt){
    if(L <= l && r <= R){
        return sum[rt];
    }
    int mid=(l+r)>>1,ans=0;
    if(L <= mid) ans=(ans+query(L,R,l,mid,lson))%p;
    if(R > mid) ans=(ans+query(L,R,mid+1,r,rson))%p;
    return ans;
}

signed main()
{
    n=read(),m=read(),p=read(),c=read();
    for(int i=1;i<=n;i++) a[i]=read();
    pre();build(1,n,1);
    int op,l,r;
    while(m--){
        op=read(),l=read(),r=read();
        if(op==0) update(l,r,1,n,1);
        if(op==1) printf("%lld\n",query(l,r,1,n,1));
    }
    return 0;
}