注意到模数被给出且非常小,做法肯定要依赖于一些与此相关的性质。找题解打表可以发现循环节长度的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==||c==||c==) c=getchar();return c;}
int gcd(int n,int m){return m==?n:gcd(m,n%m);}
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
int n,m,p,a[N];
int L[N<<],R[N<<],value[N<<][],len[N<<],pos[N<<],sum[N<<],lazy[N<<];
bool flag[N];
void up(int k)
{
int x=pos[k<<],y=pos[k<<|];
for (int i=;i<len[k];i++)
{
value[k][i]=value[k<<][x]+value[k<<|][y];
x=(x+)%len[k<<],y=(y+)%len[k<<|];
}
pos[k]=;
}
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]=,x=x*x%p;
int y=a[l];
while (y!=x) pos[k]--,flag[y]=,y=y*y%p;
do
{
value[k][len[k]++]=y;
flag[y]=;
y=y*y%p;
}while (y!=x);
return;
}
int mid=l+r>>;
build(k<<,l,mid);
build(k<<|,mid+,r);
len[k]=len[k<<]*len[k<<|]/gcd(len[k<<],len[k<<|]);
pos[k]=min(pos[k<<],pos[k<<|]);
sum[k]=sum[k<<]+sum[k<<|];
if (pos[k]==) 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<<,lazy[k]),update(k<<|,lazy[k]),lazy[k]=;}
void modify(int k,int l,int r)
{
if (L[k]==l&&R[k]==r)
{
if (pos[k]>=) update(k,);
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<<,l,L[k]+R[k]>>);
modify(k<<|,(L[k]+R[k]>>)+,r);
sum[k]=sum[k<<]+sum[k<<|];
if (pos[k]==) up(k);
}
return;
}
if (lazy[k]) down(k);
int mid=L[k]+R[k]>>;
if (r<=mid) modify(k<<,l,r);
else if (l>mid) modify(k<<|,l,r);
else modify(k<<,l,mid),modify(k<<|,mid+,r);
sum[k]=sum[k<<]+sum[k<<|];
if (pos[k]>=) 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]>>;
if (r<=mid) return query(k<<,l,r);
else if (l>mid) return query(k<<|,l,r);
else return query(k<<,l,mid)+query(k<<|,mid+,r);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj4105.in","r",stdin);
freopen("bzoj4105.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read(),m=read(),p=read();
for (int i=;i<=n;i++) a[i]=read();
build(,,n);
while (m--)
{
int op=read(),l=read(),r=read();
if (op==) modify(,l,r);
else printf("%d\n",query(,l,r));
}
return ;
}