[题解]bzoj4869 SHOI2017相逢是问候

来源:互联网 发布:淘宝美之梦睡衣 编辑:程序博客网 时间:2024/05/16 09:04

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

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

Solution

线段树。可以看得出操作有限次之后不会再变化,因为我们有扩展欧拉定理:cxcx mod ϕ(p)+ϕ(p) mod p,x>ϕ(p)
所以最多O(logP)次指数就会变为模1加1,所以我们用线段树暴力下去修改,同时记下每个区间的修改次数,如果整个区间内的修改次数超过限制就直接返回,加上快速幂的复杂度,这是三个log的算法,但是过得去。

#include<cstdio>#include<algorithm>using namespace std;template<typename T>inline void read(T &x){    T f=1;char ch=getchar();    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;    for(x=0;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';    x*=f;}typedef long long LL;const int maxn=50010;int n,m,a[maxn],cnt,c;LL mod[maxn];LL phi(LL x){    LL res=x;    for(int i=2;i*i<=x;i++){        if(!(x%i)){            res=res*(i-1)/i;            while(!(x%i))x/=i;        }    }    if(x!=1)res=res*(x-1)/x;    return res;}LL pow(LL x,LL y,LL modnum){    LL res=1;    while(y){        if(y&1)res=res*x%modnum;        y>>=1;x=x*x%modnum;    }    return res;}LL Calc(LL x,int t){    LL res=x;    if(res>mod[t])res=res%mod[t]+mod[t];    while(t--){        res=pow(c,res,mod[t]);        if(!res)res=mod[t];    }    return res%mod[0];}struct Segment_Tree{    #define lc x<<1    #define rc x<<1|1    int L[maxn<<2],R[maxn<<2];    LL sum[maxn<<2],dep[maxn<<2];    void Build(int l,int r,int x=1){        if((L[x]=l)==(R[x]=r)){            dep[x]=0;sum[x]=a[l];            return;        }        int mid=(l+r)>>1;        Build(l,mid,lc);Build(mid+1,r,rc);        dep[x]=min(dep[lc],dep[rc]);        sum[x]=(sum[lc]+sum[rc])%mod[0];    }    void Modify(int l,int r,int x=1){        if(R[x]<l||L[x]>r||dep[x]>=cnt)return;        if(L[x]==R[x])return sum[x]=Calc(a[L[x]],++dep[x]),void();        Modify(l,r,lc);Modify(l,r,rc);        dep[x]=min(dep[lc],dep[rc]);        sum[x]=(sum[lc]+sum[rc])%mod[0];    }    LL Query(int l,int r,int x=1){        if(R[x]<l||L[x]>r)return 0;        if(L[x]>=l&&R[x]<=r)return sum[x];        return (Query(l,r,lc)+Query(l,r,rc))%mod[0];    }}tree;int main(){    read(n);read(m);read(mod[0]);read(c);    while(mod[cnt]!=1)cnt++,mod[cnt]=phi(mod[cnt-1]);    mod[++cnt]=1;    for(int i=1;i<=n;i++)read(a[i]);    tree.Build(1,n);    while(m--){        int opt,l,r;        read(opt);read(l);read(r);        if(!opt)tree.Modify(l,r);        else printf("%lld\n",tree.Query(l,r));    }    return 0;}
原创粉丝点击