题目大意:
维护一个序列,支持将区间内的数x变为c^x,区间求和。
题解:
扩展欧拉定理
a^b=a^(b%phi(p)+phi(p)) ( mod p )
然后因为底数是确定的,所以一个数最多运行log(x)次就不会变了,因为phi(p)执行log(x)次后变为1
线段树暴力即可
代码:
#include<cstdio> #include<algorithm> using namespace std; int n,mod,c,m,p,cnt,a[1000005],phi[1000005],tree[1000005],minn[1000005]; int get_phi(int x){ int ans=x; for (int i=2; i*i<=x; i++) if (x%i==0){ ans=ans/i*(i-1); while (x%i==0) x/=i; } if (x!=1) ans=ans/x*(x-1); return ans; } void build(int t,int l,int r){ if (l==r){ tree[t]=a[l]; return; } int mid=(l+r)>>1; build(t<<1,l,mid); build(t<<1|1,mid+1,r); tree[t]=(tree[t<<1]+tree[t<<1|1])%mod; } int pow(int a,int b,int mod){ int ans=1; while (b){ if (b&1) ans=1ll*ans*a%mod; a=1ll*a*a%mod; b=b>>1; } return ans; } int query(int a,int times){ for (int i=times; i>=1; i--){ if (a>=phi[i]) a=a%phi[i]+phi[i]; a=pow(c,a,phi[i-1]); if (!a) a=phi[i-1]; } return a; } void change(int t,int l,int r,int x,int y){ if (l>y || r<x) return; if (minn[t]>=cnt) return; if (l==r){ minn[t]++; tree[t]=query(a[l],minn[t]); return; } int mid=(l+r)>>1; change(t<<1,l,mid,x,y); change(t<<1|1,mid+1,r,x,y); tree[t]=(tree[t<<1]+tree[t<<1|1])%mod; minn[t]=min(minn[t<<1],minn[t<<1|1]); } int query(int t,int l,int r,int x,int y){ if (l>y || r<x) return 0; if (l>=x && r<=y) return tree[t]; int mid=(l+r)>>1; return (query(t<<1,l,mid,x,y)+query(t<<1|1,mid+1,r,x,y))%mod; } int main(){ scanf("%d%d%d%d",&n,&m,&p,&c); for (int i=1; i<=n; i++) scanf("%d",&a[i]); mod=phi[0]=p; while (p!=1){ phi[++cnt]=get_phi(p); p=phi[cnt]; } phi[++cnt]=1; build(1,1,n); while (m--){ int cas,l,r; scanf("%d%d%d",&cas,&l,&r); if (cas==0) change(1,1,n,l,r); else printf("%d ",query(1,1,n,l,r)); } return 0; }