1. 程式人生 > >【bzoj4869】[Shoi2017]相逢是問候 線段樹+擴展歐拉定理

【bzoj4869】[Shoi2017]相逢是問候 線段樹+擴展歐拉定理

bin 一行 bit scan -i ont 信息 can 問題

Description

Informatikverbindetdichundmich.

信息將你我連結。B君希望以維護一個長度為n的數組,這個數組的下標為從1到n的正整數。一共有m個操作,可以

分為兩種:0 l r表示將第l個到第r個數(al,al+1,...,ar)中的每一個數ai替換為c^ai,即c的ai次方,其中c是

輸入的一個常數,也就是執行賦值ai=c^ai1 l r求第l個到第r個數的和,也就是輸出:sigma(ai),l<=i<=rai因為

這個結果可能會很大,所以你只需要輸出結果mod p的值即可。

Input

第一行有三個整數n,m,p,c,所有整數含義見問題描述。

接下來一行n個整數,表示a數組的初始值。

接下來m行,每行三個整數,其中第一個整數表示了操作的類型。

如果是0的話,表示這是一個修改操作,操作的參數為l,r。

如果是1的話,表示這是一個詢問操作,操作的參數為l,r。

1 ≤ n ≤ 50000, 1 ≤ m ≤ 50000, 1 ≤ p ≤ 100000000, 0 < c <p, 0 ≤ ai < p

Output

對於每個詢問操作,輸出一行,包括一個整數表示答案mod p的值。

Sample Input

4 4 7 2
1 2 3 4
0 1 4
1 2 4
0 1 4
1 1 3

Sample Output

0
3

Sol

根據擴展歐拉定理,參照bzoj3884,我們發現某個數字做一定次數比操作之後就不會變了,這個次數在\(logn\)

左右,所以就可以用線段樹維護區間和以及這個區間的數字有沒有都處理完畢,然後直接維護即可。時間復雜度\(nlog^3n\),由於本題數據範圍\(50000\),所以可以通過。

註意細節:判斷某個數字有沒有超過\(\varphi(p)\),以及\(\varphi(1)=\varphi(2)=1\),但是我們必須要在\(p=1\)的時候才能停止。

Code

#include <bits/stdc++.h>
using namespace std;
int n,m,P,c,K,op,l,r,a[50005],p[50005],V[50005],pr[50005],tot,sm[200005],mn[200005];
int phi(int x)
{
    int res=x;
    for(int i=1;pr[i]*pr[i]<=x;i++)
    {
        if(x%pr[i]) continue;
        res-=res/pr[i];
        while(x%pr[i]==0) x/=pr[i];
    }
    if(x>1) res-=res/x;return res;
}
void build(int x,int l,int r)
{
    if(l==r){scanf("%d",&a[l]);sm[x]=a[l]%P;mn[x]=0;return;}
    int M=(l+r)>>1;build(x<<1,l,M);build(x<<1|1,M+1,r);
    sm[x]=(sm[x<<1]+sm[x<<1|1])%P;mn[x]=min(mn[x<<1],mn[x<<1|1]);
}
int ksm(int a,int b,int P,bool &f)
{
    int res=1;bool gg=0;
    for(;b;b>>=1,a=1ll*a*a%P)
    {
        if(b&1) f|=(gg|(1ll*res*a>=P)),res=1ll*res*a%P;
        if(1ll*a*a>=P) gg=1;
    }
    return res;
}
int cal(int dep,int x)
{
    int res=x;if(res>=p[dep]) res=res%p[dep]+p[dep];
    while(dep)
    {
        dep--;bool flag=0;
        res=ksm(c,res,p[dep],flag);
        if(flag) res+=p[dep];
    }
    return res%p[dep];
}
void upd(int x,int l,int r,int b,int e)
{
    if(mn[x]>=K) return;
    if(l==r){mn[x]++;sm[x]=cal(mn[x],a[l]);return;}
    int M=(l+r)>>1;
    if(b<=M) upd(x*2,l,M,b,e);if(e>M) upd(x*2+1,M+1,r,b,e);
    sm[x]=(sm[x<<1]+sm[x<<1|1])%P;mn[x]=min(mn[x<<1],mn[x<<1|1]);
}
int que(int x,int l,int r,int b,int e)
{
    if(b<=l&&r<=e) return sm[x];
    int M=(l+r)>>1;
    return ((b<=M?que(x*2,l,M,b,e):0)+(e>M?que(x*2+1,M+1,r,b,e):0))%P;
}
int main()
{
    for(int i=2;i<=50000;i++)
    {
        if(!V[i]) pr[++tot]=i;
        for(int j=1;j<=tot&&i*pr[j]<=50000;j++){V[i*pr[j]]=1;if(i%pr[j]==0) break;}
    }
    scanf("%d%d%d%d",&n,&m,&P,&c);
    p[0]=P;while(p[K]!=1){++K;p[K]=phi(p[K-1]);}p[++K]=1;
    for(build(1,1,n);m--;)
    {
        scanf("%d%d%d",&op,&l,&r);
        if(op==0) upd(1,1,n,l,r);
        else printf("%d\n",que(1,1,n,l,r));
    }
}

【bzoj4869】[Shoi2017]相逢是問候 線段樹+擴展歐拉定理