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
一句话:
/*
这道题真的 难受死了
1.直接放弃 因为要更新5e4次,一共5e4个点--->我放弃了,不可能的,如果区间更新的话,还是要算到每一个点,所以
还是点更新,复杂度根本不够怎么办,肯定有别的方法,自己没学---->好,看看题解
mmp,每个点更新很少的几次之后就不会更新了(标记区域,之后就相当于是区间更新,到了定值,就不更新了)
2.欧拉定理 (1)原理是什么 (2)条件是什么 (3)最后终结在哪里
(1)到现在也不是很清楚,只知道,gcd(a,p)==1,时降幂的原理,也就是 费马小定理,至于拓展的,记住结论吧
(2)条件是 a^(x) mod(p) -----> 变为[a^(x mod(phi(p))+x>=phi(p)?phi(p):0]mod(p)
(2.2)还有就是x>=phi(p)这个条件, 这个x可不是 mod(*) 之后的值,它是之前的值,所以不能直接比较,只能在快速幂,那里比较一下
(3) 一个p,经过至多 log(p)次之后,就会变成1, 我们知道任意一个数值mod(1)都是0,所以这也就奠定了终结的条件
(3.2)但是最后是直接算到 phi(*)=1,就结束了么,出题人就是这么错的,导致少了一层 比如 2^(2^0) mod3 它是2
而2^(2^(2^0)) mod 3,为 1,哪个才是定值呢,应该是后面一个,因为 最后一个要是mod phi(1)的话,说明
第一个加的C 肯定就是 mod 1了,也就是肯定是恒定的了, 但是如果第一个C mod2的话,那就不一定了,所以
最后要算到phi(1),也就是 val%phi(1)+ [phi(1)], C^(*) mod 1,这样的话,肯定就是定值了,
如果多算几层,肯定也行,只不过,人都喜欢精简,也就是最小的层数到底是几
*/
#include<bits/stdc++.h>
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
typedef long long ll;
using namespace std;
const int N=50005;
int n,m,P,DEP;
ll a[N],c,Phi[N];
int dep[N];
struct trment{
int l,r;
ll sum;
int cnt;
}tr[N<<2];
void pushup(int id){
tr[id].sum=(tr[id<<1].sum+tr[id<<1|1].sum)%P;
tr[id].cnt=min(tr[id<<1].cnt,tr[id<<1|1].cnt);
}
int phi(int x){
int m=sqrt(x+0.5);
int ans=x;
for(int i=2;i<=m;i++){
if(x%i==0){
ans-=ans/i;
while(x%i==0)x/=i;
}
}
if(x>1)ans-=ans/x;
return ans;
}
ll pow_(int flag,int n,ll mod){
ll ans=1,base=c;
if(ans>=mod)flag=1;
while (n){
if (n&1){
ans=ans*base%mod;if(ans>=mod)flag=1;
}
n>>=1;
base=base*base%mod; if(base>=mod)flag=1;
}
return ans;
}
ll Cal(int t,ll x){//????判断大小
ll res=x;
if(res>Phi[t])res=res%Phi[t]+Phi[t];
while(t){
int flag=0;//只能这么将就了,不会别的了
res=pow_(flag,res,Phi[t-1]);
if(flag||res>=Phi[t-1]||res==0)res+=Phi[t-1];
//防止有一种情况就是取余之后,直接为0了,那他肯定是 Phi[t-1]的倍数,所以需要判断是否为0
//那他肯定大于Phi[t-1],但还是不能保证它是100%正确
//%Phi[t-1]的话,那第二个判断条件就没有用了,所以只能在快速幂里面判断
t--;
}
return res%Phi[0];
}
void build(int id,int l,int r){
tr[id].l=l,tr[id].r=r;
if (l==r){
tr[id].sum=a[l],tr[id].cnt=0;
return;
}
int mid=(l+r)>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
pushup(id);
}
//点更新
void update(int id,int l,int r){
if(tr[id].cnt>=DEP)return;
if(tr[id].l==tr[id].r){
tr[id].cnt++;
tr[id].sum=Cal(tr[id].cnt,a[tr[id].l]);
return;
}
int mid=(tr[id].l+tr[id].r)>>1;
if(r<=mid)update(ls(id),l,r);
else if(l>mid)update(rs(id),l,r);
else{
update(ls(id),l,mid);
update(rs(id),mid+1,r);
}
pushup(id);
}
ll query(int id,int l,int r){
if(l<=tr[id].l&&r>=tr[id].r)
return tr[id].sum;
int mid=(tr[id].l+tr[id].r)>>1;
if(r<=mid)return query(ls(id),l,r);
else if(l>mid)return query(rs(id),l,r);
else {
ll ans1=query(ls(id),l,mid);
ll ans2=query(rs(id),mid+1,r);
return (ans1+ans2)%P;
}
}
void Pre_Phi(int p){
int t=phi(p);
Phi[DEP=0]=p;
while (Phi[DEP]!=1){
Phi[++DEP]=t;
Phi[DEP]=phi(Phi[DEP-1]);
}
Phi[++DEP]=1;
}
int main(){
scanf("%d %d %d %lld",&n,&m,&P,&c);
Pre_Phi(P);
for (int i=1;i<=n;i++)
scanf("%lld",&a[i]);
build(1,1,n);
int opt,l,r;
while (m--){
scanf("%d %d %d",&opt,&l,&r);
if (!opt) update(1,l,r);
else printf("%lld\n",query(1,l,r));
}
return 0;
}