题目大意
给定一个序列。要求满足区间取
cai
(
c
为定值),区间求和(模
Solution
首先看着就像线段树,这种题一般都有一个暴力不会超时的性质。
对这题来说:
首先要知道如下欧拉定理EXT:
ab≡ab mod φ(p) + φ(p)(modp),b≥φ(p)
通过不断展开被修改的数,我们可以发现(证明)在一定次数 O(log p) 后便不会再改变。这样只要暴力修改,改到区间都不用修改就跳过就可以了。
要注意一些地方:
1.据说出题人就漏了这里:计算 φ(p),φ(φ(p))…… 时要算到 φ(1)=1 ,而不能只算到 φ(2)=1 。因为若考虑原序列中出现了 0 ,那么在某个
ccc0 mod p=c(cc0 mod 2 + 2) mod p=c(c mod 2 + 2) mod p
和
cc0 mod p=c mod p
不一定相等,例如 c=2,p=3 时,上面为 1 ,下面为
那么如果多展开一层,展开后两项(同上)只能是
ccc0 mod 2=c(cc0 mod 1 + 1) mod p=c mod p
和
cc0 mod p=c mod p
故展开后两式相同,结果相等。其余项展开到底后均有
...c(c... mod 1+1) mod 2
的形式。综上所述,只需计算至 φ(1)=1 即可。
2.为了保证 b≥φ(p) 快速幂加上一些修改判断即可。(听说只要判一层然后可以拿取余后的数接着判,经检验正确率很高QwQ)
#include<set>
#include<map>
#include<queue>
#include<cmath>
#include<string>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rep(i,a,b) for (int i=a; i<=b; i++)
#define per(i,a,b) for (int i=a; i>=b; i--)
#define debug(x) {cout<<(#x)<<" "<<x<<endl;}
using namespace std;
typedef long long LL;
inline int read() {
int x=0,f=1; char ch=getchar();
while (!(ch>='0'&&ch<='9')) {if (ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9') {x=x*10+(ch-'0'); ch=getchar();}
return x*f;
}
const int N = 50005;
const int M = 10005;
int n,m,P,c,k=0;
int a[N],p[N];
bool np[M];
int tot=0,prime[M];
int tr[N<<2],aux[N<<2];
inline void Prep() {
rep(i,2,M-1) {
if (!np[i]) prime[++tot]=i;
rep(j,1,tot) {
if (i*prime[j]>=M) break;
np[i*prime[j]]=1;
if (i%prime[j]==0) break;
}
}
}
inline int phi(int x) {
int ret=x;
for (int i=1;prime[i]*prime[i]<=x;i++) {
if (x%prime[i]) continue;
ret=ret-ret/prime[i];
while (x%prime[i]==0) x/=prime[i];
}
if (x>1) ret=ret-ret/x;
return ret;
}
inline void pushup(int x) {
tr[x]=(tr[x<<1]+tr[x<<1|1])%P;
aux[x]=min(aux[x<<1],aux[x<<1|1]);
}
inline void Build(int x,int l,int r) {
if (l==r) {
a[l]=read(); tr[x]=a[l]%P; aux[x]=0; return;
}
int mid=(l+r)>>1;
Build(x<<1,l,mid); Build(x<<1|1,mid+1,r);
pushup(x);
}
inline int pow(int a,int b,int P,bool &flag) {
int ret=1;
bool big=0;
while (b) {
if (b&1) {flag|=big|((LL)ret*a>=P); ret=(LL)ret*a%P;}
if ((LL)a*a>=P) big=1;
a=(LL)a*a%P; b>>=1;
}
return ret;
}
inline int Calc(int dep,int x) {
int ret=x; if (ret>=p[dep]) ret=ret%p[dep]+p[dep];
while (dep) {
dep--;
bool flag=0;
ret=pow(c,ret,p[dep],flag);
if (flag) ret+=p[dep];
}
return ret%p[dep];
}
inline void modify(int x,int l,int r,int ll,int rr) {
if (l>rr||r<ll) return;
if (aux[x]>=k) return;
if (l==r) {
aux[x]++;
tr[x]=Calc(aux[x],a[l]);
return;
}
int mid=(l+r)>>1;
modify(x<<1,l,mid,ll,rr); modify(x<<1|1,mid+1,r,ll,rr);
pushup(x);
}
inline int query(int x,int l,int r,int ll,int rr) {
if (l>rr||r<ll) return 0;
if (l>=ll&&r<=rr) return tr[x];
int mid=(l+r)>>1;
return (query(x<<1,l,mid,ll,rr)+query(x<<1|1,mid+1,r,ll,rr))%P;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("verbinden.in","r",stdin);
freopen("verbinden.out","w",stdout);
#endif
Prep(); n=read(),m=read(),P=read(),c=read();
p[0]=P; while (p[k]!=1) {++k;p[k]=phi(p[k-1]);} p[++k]=1;
Build(1,1,n);
while (m--) {
int opt=read(),l=read(),r=read();
if (!opt) modify(1,1,n,l,r);
else printf("%d\n",query(1,1,n,l,r));
}
return 0;
}