解析
洛谷你恶事做尽!
第三个tag在LOJ、bzoj等都是不需要的…
但在洛谷三只log根本过不去…
我谔谔。
如果做过 上帝与集合的正确用法 ,那么本题就并不难了。
打个表就可以发现,不断取欧拉函数的上限只有log级别,这使得我们暴力修改线段树复杂度就是正确的。
然后就做完了?
不!
这题也暴露了我数论知识及其不扎实的事实,拓展欧拉定理的完整内容为:
x
b
≡
x
b
(
m
o
d
p
)
(
b
<
φ
(
p
)
)
x^b\equiv x^b\pmod p(b<\varphi(p))
xb≡xb(modp)(b<φ(p))
x
b
≡
x
b
%
φ
(
p
)
+
φ
(
p
)
(
m
o
d
p
)
(
b
≥
φ
(
p
)
)
x^b\equiv x^{b\%\varphi(p)+\varphi(p)}\pmod p(b\ge\varphi(p))
xb≡xb%φ(p)+φ(p)(modp)(b≥φ(p))
上帝那个题之所以可以直接当第二条来做,是因为我们认为要求的那个东西绝对大于
φ
(
p
)
\varphi(p)
φ(p)的。
本题则不然。
所以还需要在上一层快速幂的时候判断以下是否取过模,即是否达到了
φ
(
p
)
\varphi(p)
φ(p)。
总复杂度
O
(
n
log
3
n
)
O(n\log^3n)
O(nlog3n)。
然后就做完了?
不!
这个代码已经可以在LOJ上AC,但在洛谷打死也T#11。
我们发现,我们每次快速幂的时候的底数都是固定的,模数也就只是
p
p
p 不断取欧拉函数得到的
O
(
log
)
O(\log)
O(log) 个数而已。
所以我们可以对每个模数预处理出光速幂,这样就可以把快速幂的log拿掉了。
别忘了快速幂里还要判断是否取模。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
using namespace std;
const int N=4e5+100;
const int M=2e5+100;
const int inf=1e9;
inline ll read(){
ll x(0),f(1);char c=getchar();
while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}
while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
bool flag;
const int B=4e4+100;
struct KSM{
int c,mod,w;
ll mi[B],Mi[B];
bool jd[B],Jd[B];
void init(int C,int Mod){
c=C;mod=Mod;
w=sqrt(2*mod);
mi[0]=1;
for(int i=1;i<=w;i++){
mi[i]=mi[i-1]*c;
jd[i]=jd[i-1];
if(mi[i]>=mod){
mi[i]%=mod;
jd[i]=1;
}
}
Mi[0]=1;
Mi[1]=mi[w];Jd[1]=jd[w];
for(int i=2;i<=2*mod/w;i++){
Mi[i]=Mi[i-1]*Mi[1];
Jd[i]=Jd[i-1];
if(Mi[i]>=mod){
Mi[i]%=mod;
Jd[i]=1;
}
}
}
inline ll calc(ll k){
if(k>2*mod){
printf("k=%lld mod=%d\n",k,mod);
}
ll res=Mi[k/w]*mi[k%w];
flag=Jd[k/w]|jd[k%w];
if(res>=mod){
res%=mod;
flag=1;
}
//printf(" k=%lld mod=%d %d %d jd=%d %d res=%lld\n",k,mod,k/w,k%w,Jd[k/w],jd[k%w],res);
return res;
}
}ksm[40];
inline int getphi(int x){
int ans=x,top=sqrt(x);
for(int i=2;i<=top;i++){
if(x%i) continue;
ans=1ll*ans*(i-1)/i;
while(x%i==0) x/=i;
}
if(x>1){
ans=1ll*ans*(x-1)/x;
}
return ans;
}
int n,m,mod,c;
int w[105];
inline int Solve(int k,int lim,int x,int mod){
if(k>lim){
flag=x>=mod;
return x%mod;
}
if(mod<=1){
flag=1;
return 0;
}
int phi=w[k],pre=Solve(k+1,lim,x,phi);
//printf(" k=%d pre=%d phi=%d mod=%d flag=%d mymod=%d\n",k,pre,phi,mod,flag,ksm[k].mod);
//if(pre+flag*phi>ksm[k].mod*2){
// printf(" k=%d pre=%d phi=%d mod=%d flag=%d mi=%d mymod=%d\n",k,pre,phi,mod,flag,pre+flag*phi,ksm[k].mod);
// }
return ksm[k].calc(pre+flag*phi)%mod;
}
inline int solve(int lim,int x,int mod){
return Solve(1,lim,x,mod);
}
int top;
void calc(int k,int x){
int phi=getphi(x);
w[k]=phi;
if(x==1){
top=k;
return;
}
ksm[k].init(c,x);
calc(k+1,phi);
}
int a[N];
#define mid ((l+r)>>1)
#define ls (k<<1)
#define rs (k<<1|1)
int mn[N<<2],sum[N<<2];
inline void pushup(int k){
mn[k]=min(mn[ls],mn[rs]);
sum[k]=(sum[ls]+sum[rs])%mod;
return;
}
void build(int k,int l,int r){
if(l==r){
sum[k]=a[l];
return;
}
build(ls,l,mid);
build(rs,mid+1,r);
pushup(k);
}
int ask(int k,int l,int r,int x,int y){
if(x<=l&&r<=y) return sum[k];
int res(0);
if(x<=mid) res+=ask(ls,l,mid,x,y);
if(y>mid) res+=ask(rs,mid+1,r,x,y);
res%=mod;
return res;
}
void change(int k,int l,int r,int x,int y){
if(mn[k]>=top) return;
if(l==r){
mn[k]++;
sum[k]=solve(mn[k],a[l],mod);
//printf("k=%d pos=%d mn=%d sum=%d\n",k,l,mn[k],sum[k]);
return;
}
if(x<=mid) change(ls,l,mid,x,y);
if(y>mid) change(rs,mid+1,r,x,y);
pushup(k);
}
void write(int x){
if(x>9) write(x/10);
putchar('0'+x%10);
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
n=read();m=read();mod=read();c=read();
w[0]=mod;
calc(1,mod);
//printf("top=%d\n",top);
//for(int i=0;i<mod;i++){
// for(int j=0;j<=top;j++) printf("x=%d tim=%d %lld\n",i,j,solve(j,i,mod));
//}
//return 0;
for(int i=1;i<=n;i++) a[i]=read();
build(1,1,n);
for(int i=1;i<=m;i++){
int op=read(),l=read(),r=read();//ok;
if(op==0) change(1,1,n,l,r);
else{
write(ask(1,1,n,l,r));
putchar('\n');
}
}
return 0;
}
/*
*/