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
线段树。可以看得出操作有限次之后不会再变化,因为我们有扩展欧拉定理:
cx≡cx 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;
}