题意:
给定一个长度为n的序列A,常数p和c。你需要支持m次操作,分为两种:
- 0 l r:将区间$[l,r]$中所有$a_{i}$替换成$c^{a_{i}}$。
- 1 l r:查询区间$[l,r]$中所有数的和,对p取模。
$n,mleq 50000,c<pleq 10^{8}$。
题解:
一般像这种看起来根本没法维护的线段树题都是操作几次就变常数了,于是我们考虑一下这个操作的性质。
首先引入拓展欧拉定理:
$a^{b} mod p=egin{cases}a^{b},b<phi(p) \ a^{b mod phi(p)+phi(p)},bgeq phi(p) \ end{cases}$
注意到$phi^{k}(p)$在$k=30$左右的时候就会变成1,而任何数对1取模都等于0。
也就是说,当某个位置i被操作k次之后,它初始的$a_{i}$会因为对1取模而消失掉。
那么当一个区间被操作k次之后,它包含的所有$a_{i}$都是同一个值,且无论再操作多少次都还是这个值。
所以我们只需要预处理出每个位置操作k次之内的答案,对于一次修改,我们暴力递归修改次数小于k的区间即可。
(注意不能对于一次查询暴力递归,可能会出现没有修改只有查询的情况)
每个区间最多被暴力更新k次,所以线段树的复杂度不会超过$O(nlog{n}k)$,其中k是$log{p}$级别的。
但是预处理复杂度是$O(nk^{3})$的,瓶颈在于快速幂。
这里面有一个小技巧:利用BSGS的思想预处理出$0leq i<10000,c^{i}$的值和$0leq i<10000,c^{10000i}$的值,两边拼一下即可。
在快速幂里还需要判断一下指数b是否小于$phi(p)$,注意到$2^{2^{2^{2^{2}}}}$肯定已经超过p了,所以只在5以内判一下即可,不会爆longlong。
但最后几个欧拉函数在p很大时貌似都等于$2^{i}$,所以直接在返回值等于0的时候返回$phi(i)$也能过。
总复杂度$O(nlog^{2}{p})$。
套路:
- 看起来没法维护的线段树问题$ ightarrow$考虑是否可以在有限次操作后变为常数。
- 拓展欧拉定理:$a^{b} mod p=egin{cases}a^{b},b<phi(p) \ a^{b mod phi(p)+phi(p)},bgeq phi(p) \ end{cases}$
- 预处理快速幂:利用BSGS思想分块预处理。
代码:

#include<bits/stdc++.h> #define maxn 200005 #define maxm 10000 #define inf 0x7fffffff #define ll long long #define rint register ll #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; ll mod,A[maxn][50],phi[maxn]; ll bs[50][maxn],gs[50][maxn]; ll pri[maxn],ish[maxn]; inline ll read(){ ll x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } struct Segment{ ll mn[maxn<<2],lz[maxn<<2],sum[maxn<<2]; inline void build(ll l,ll r,ll k){ if(l==r){mn[k]=1,sum[k]=A[l][1];return;} ll mid=l+r>>1; build(l,mid,k<<1),build(mid+1,r,k<<1|1); mn[k]=min(mn[k<<1],mn[k<<1|1]); sum[k]=(sum[k<<1]+sum[k<<1|1])%mod; } inline void pushdown(ll k){ if(lz[k]){ lz[k<<1]+=lz[k],lz[k<<1|1]+=lz[k]; mn[k<<1]+=lz[k],mn[k<<1|1]+=lz[k]; lz[k]=0; } } inline void force(ll l,ll r,ll k){ if(mn[k]>phi[0]){sum[k]=A[1][phi[0]]*(r-l+1)%mod;return;} if(l==r){sum[k]=A[l][mn[k]];return;} pushdown(k); ll mid=l+r>>1; force(l,mid,k<<1),force(mid+1,r,k<<1|1); sum[k]=(sum[k<<1]+sum[k<<1|1])%mod; } inline void upd(ll x,ll y,ll l,ll r,ll k){ if(x<=l && r<=y){ mn[k]++,lz[k]++; if(mn[k]<=phi[0]) force(l,r,k); else sum[k]=A[1][phi[0]]*(r-l+1)%mod; return; } pushdown(k); ll mid=l+r>>1; if(x<=mid) upd(x,y,l,mid,k<<1); if(y>mid) upd(x,y,mid+1,r,k<<1|1); mn[k]=min(mn[k<<1],mn[k<<1|1]); sum[k]=(sum[k<<1]+sum[k<<1|1])%mod; } inline ll qry(ll x,ll y,ll l,ll r,ll k){ if(x<=l && r<=y) return sum[k]; pushdown(k); ll mid=l+r>>1,res=0; if(x<=mid) res=(res+qry(x,y,l,mid,k<<1))%mod; if(y>mid) res=(res+qry(x,y,mid+1,r,k<<1|1))%mod; return res; } }tr; inline ll pw(ll a,ll b,ll mo){ll r=1;while(b)r=(b&1)?r*a%mo:r,a=a*a%mo,b>>=1;return r;} inline ll pwp(ll mo,ll b){return bs[mo][b%maxm]*gs[mo][b/maxm]%phi[mo];} inline void Init(ll x){ for(ll i=2;i<x;i++){ if(!ish[i]) pri[++pri[0]]=i; for(ll j=1;j<=pri[0];j++){ if(pri[j]*i>=x) break; ish[pri[j]*i]=1; if(i%pri[j]==0) break; } } } inline void init(ll p){ phi[++phi[0]]=p; if(p==1){phi[++phi[0]]=1;return;} ll x=p,res=p; for(ll i=1;i<=pri[0];i++) if(x%pri[i]==0){ res=(res-res/pri[i]); while(x%pri[i]==0) x/=pri[i]; } if(x>1) res=(res-res/x); init(res); } inline ll solve(ll a,ll t,ll end){ if(t==end) return a; ll ft=solve(a,t+1,end); if(ft>=phi[t+1]) ft=ft%phi[t+1]+phi[t+1]; ll res=pwp(t,ft); return (!res)?phi[t]:res; } int main(){ ll n=read(),m=read(); mod=read(); ll c=read(); Init(maxm),init(mod); for(ll t=1;t<=phi[0];t++){ for(ll i=0;i<maxm;i++) bs[t][i]=pw(c,i,phi[t]); for(ll i=0;i<maxm;i++) gs[t][i]=pw(c,i*maxm,phi[t]); } for(ll i=1;i<=n;i++){ ll x=read(); for(ll j=1;j<=phi[0];j++) A[i][j]=solve(x,1,j); } //fgx; tr.build(1,n,1); while(m--){ ll op=read(),l=read(),r=read(); if(op==0) tr.upd(l,r,1,n,1); else printf("%lld ",tr.qry(l,r,1,n,1)); } return 0; }