注意到模数被给出且非常小,做法肯定要依赖于一些与此相关的性质。找题解打表可以发现循环节长度的lcm不超过60。
考虑怎么用线段树维护循环。对线段树上每个点维护这段区间的循环节、在循环中的位置,如果未进入环特殊记录;每次修改对于未进入环的暴力修改,已进入环的更新在循环节上的位置即可。对于修改经过的节点暴力重构循环节。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long #define N 100010 char getc(){char c=getchar();while (c==10||c==13||c==32) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,p,a[N]; int L[N<<2],R[N<<2],value[N<<2][61],len[N<<2],pos[N<<2],sum[N<<2],lazy[N<<2]; bool flag[N]; void up(int k) { int x=pos[k<<1],y=pos[k<<1|1]; for (int i=0;i<len[k];i++) { value[k][i]=value[k<<1][x]+value[k<<1|1][y]; x=(x+1)%len[k<<1],y=(y+1)%len[k<<1|1]; } pos[k]=0; } void build(int k,int l,int r) { L[k]=l,R[k]=r; if (l==r) { sum[k]=a[l]; int x=a[l]; while (!flag[x]) flag[x]=1,x=x*x%p; int y=a[l]; while (y!=x) pos[k]--,flag[y]=0,y=y*y%p; do { value[k][len[k]++]=y; flag[y]=0; y=y*y%p; }while (y!=x); return; } int mid=l+r>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); len[k]=len[k<<1]*len[k<<1|1]/gcd(len[k<<1],len[k<<1|1]); pos[k]=min(pos[k<<1],pos[k<<1|1]); sum[k]=sum[k<<1]+sum[k<<1|1]; if (pos[k]==0) up(k); } void update(int k,int x){sum[k]=value[k][pos[k]=(pos[k]+x)%len[k]],lazy[k]+=x;} void down(int k){update(k<<1,lazy[k]),update(k<<1|1,lazy[k]),lazy[k]=0;} void modify(int k,int l,int r) { if (L[k]==l&&R[k]==r) { if (pos[k]>=0) update(k,1); else if (L[k]==R[k]) sum[k]=sum[k]*sum[k]%p,pos[k]++; else { pos[k]++; if (lazy[k]) down(k); modify(k<<1,l,L[k]+R[k]>>1); modify(k<<1|1,(L[k]+R[k]>>1)+1,r); sum[k]=sum[k<<1]+sum[k<<1|1]; if (pos[k]==0) up(k); } return; } if (lazy[k]) down(k); int mid=L[k]+R[k]>>1; if (r<=mid) modify(k<<1,l,r); else if (l>mid) modify(k<<1|1,l,r); else modify(k<<1,l,mid),modify(k<<1|1,mid+1,r); sum[k]=sum[k<<1]+sum[k<<1|1]; if (pos[k]>=0) up(k); } int query(int k,int l,int r) { if (L[k]==l&&R[k]==r) return sum[k]; if (lazy[k]) down(k); int mid=L[k]+R[k]>>1; if (r<=mid) return query(k<<1,l,r); else if (l>mid) return query(k<<1|1,l,r); else return query(k<<1,l,mid)+query(k<<1|1,mid+1,r); } int main() { #ifndef ONLINE_JUDGE freopen("bzoj4105.in","r",stdin); freopen("bzoj4105.out","w",stdout); const char LL[]="%I64d "; #else const char LL[]="%lld "; #endif n=read(),m=read(),p=read(); for (int i=1;i<=n;i++) a[i]=read(); build(1,1,n); while (m--) { int op=read(),l=read(),r=read(); if (op==0) modify(1,l,r); else printf("%d ",query(1,l,r)); } return 0; }