来自FallDream的博客,未经允许,请勿转载,谢谢。
B 君希望以维护一个长度为 n 的数组,这个数组的下标为从 1 到 n 的正整数。
一共有 m 个操作,可以分为两种:
- 0 l r 表示将第 l 个到第 r 个数(al al+1 ... ar)中的每一个数 ai 替换为$c^{ai}$,即 c 的 ai 次方,其中 c 是输入的一个常数,也就是执行赋值
$ai=c^{ai}$
- 1 l r 求第 l 个到第 r 个数的和,也就是输出:$sum_{i=l}^{r} ai$
因为这个结果可能会很大,所以你只需要输出结果 mod p 的值即可。
1 ≤ n ≤ 50000; 1 ≤ m ≤ 50000; 1 ≤ p ≤ 100000000; 0 < c <p; 0 ≤ ai < p。
这道题真的折磨人....
底数与模数不互质,不能直接用欧拉定理
扩展欧拉定理 当$b>varphi(m)$时 $ a^{b}= a^{{ varphi(m) +bmodvarphi(m) }}(mod m) $
然后递归下去就能计算。
通过这个式子可以发现,一个数在操作最多log次之后就会变成定值,所以直接暴力计算即可。
然后直接计算是log三方的,所以考虑把快速幂的log压掉
因为指数不超过2p,所以预处理每个模数时c的0到$2^{14}$次方的答案,和c的$i*2^{14},i=1-2^{14}$次方的答案,查询时候直接并起来即可。
然后有个地方就是 phi(1)=1 所以最后要加上去一个1,不然会wa 233
复杂度$nlog^{2}n$
#include<iostream> #include<cstdio> #include<cmath> #define MN 50000 #define N 16384 #define ll long long #define rint register int #define getchar() (*S++) char B[1<<26],*S=B; using namespace std; 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; } bool b[10005];int s[10005],cnt=0; int n,m,c,p,a[MN+5],Phi[30],rt,Rt,num[MN+5],Mx,pw[40][N+5],Pw[40][N+5]; struct Tree{int l,r,val,x;}T[MN*4+5]; inline void pushup(int x){int l=x<<1,r=x<<1|1;T[x].val=T[l].val+T[r].val;T[x].x=(T[l].x+T[r].x)%p;} void build(int x,int l,int r) { if((T[x].l=l)==(T[x].r=r)){T[x].x=a[l];T[x].val=1;return;} int mid=l+r>>1; build(x<<1,l,mid);build(x<<1|1,mid+1,r); pushup(x); } inline int pow(int k,int mod){return !k?1:1LL*Pw[mod][k&(N-1)]*pw[mod][k>>14]%Phi[mod];} inline int Calc(int x,int p) { int res=x%Phi[p];bool b=x>=Phi[p]; for(int i=p;i;--i) { if(b) res+=Phi[i];int pre=res; res=pow(res,i-1); if(!b && res >= log(Phi[i-1]/log(c))) b=1; } return res; } inline void Mark(int x) { if(T[x].l==T[x].r) { T[x].x=Calc(a[T[x].l],++num[T[x].l]); if(num[T[x].l]==Mx) T[x].val=0; } else { if(T[x<<1].val) Mark(x<<1); if(T[x<<1|1].val) Mark(x<<1|1); pushup(x); } } void Modify(int x,int l,int r) { if(!T[x].val) return; if(T[x].l==l&&T[x].r==r) {Mark(x);return;} int mid=(T[x].l+T[x].r)>>1; if(r<=mid) Modify(x<<1,l,r); else if(l>mid) Modify(x<<1|1,l,r); else Modify(x<<1,l,mid),Modify(x<<1|1,mid+1,r); pushup(x); } int Query(int x,int l,int r) { if(T[x].l==l&&T[x].r==r) return T[x].x; int mid=T[x].l+T[x].r>>1; if(r<=mid) return Query(x<<1,l,r); else if(l>mid) return Query(x<<1|1,l,r); else return (Query(x<<1,l,mid)+Query(x<<1|1,mid+1,r))%p; } int GetPhi(int x) { int PHI=x; for(int i=1;i<=cnt&&s[i]<=x;++i) if(x%s[i]==0) { PHI=PHI/s[i]*(s[i]-1); do x/=s[i]; while(x%s[i]==0); } if(x>1) PHI=PHI/x*(x-1); return PHI; } int main() { fread(B,1,1<<26,stdin); n=read();m=read();p=read();c=read(); for(rint i=1;i<=n;++i) a[i]=read(); build(1,1,n);int sq=sqrt(p); for(int i=2;i<=sq;++i) { if(!b[i]) s[++cnt]=i; for(int j=1;s[j]*i<=sq;++j) { b[s[j]*i]=1; if(i%s[j]==0) break; } } for(Phi[Mx=0]=p;Phi[Mx]>1;++Mx,Phi[Mx]=GetPhi(Phi[Mx-1]));Phi[++Mx]=1; for(rint i=0;i<Mx-1;++i) { Pw[i][0]=pw[i][0]=1; for(rint j=1;j<=N;++j) Pw[i][j]=1LL*Pw[i][j-1]*c%Phi[i]; for(rint j=1;j<=N;++j) pw[i][j]=1LL*pw[i][j-1]*Pw[i][N]%Phi[i]; } for(rint i=1;i<=m;++i) { int op=read(),l=read(),r=read(); if(!op) Modify(1,l,r); else printf("%d ",Query(1,l,r)); } return 0; }