测试地址:平方运算
题目大意:维护一个序列,再给定一个模数,要求支持两个操作:1.将一个区间内的修改为。2.求。
做法:本题需要用到线段树+找规律。
看到这种特别诡异的区间操作,不能直接用线段树维护,那肯定又要分析操作的性质了。注意到因为有一个模数,所以平方很多次之后一定会进入一个循环,根据打表发现数据中所有的都满足两个性质:
1.所有数操作至多不超过步就会进入一个循环。
2.所有循环的循环节长度的LCM不超过。
于是对于操作区间中每一个没有进入循环的数,我们就暴力算出它的值,从它更新到根,这一个部分的总时间复杂度是。而对于一个区间,如果该区间内所有数都已经进入循环了,那么整个区间数字和也有循环,长度就是每个数所在循环的长度的LCM,前面我们已经得出这个数不超过了。那么我们每次在向上更新时,应该计算出该区间节点在循环节内每个位置的和,这个可以根据它的左右子区间的信息合并,每更新一个节点的复杂度是。而在之后如果再更新到这整个区间,就可以修改每一个涉及到的整区间的信息了,因此这个部分的时间复杂度为。那么我们就完成了这一题。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,loop[10010]={0},looplen[400010]={0},now[400010]={0},st[10010];
int len[10010]={0},vis[10010]={0},pos[10010]={0},tag[400010]={0};
ll p,a[100010],loopsum[400010][62],seg[400010]={0};
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
int lcm(int a,int b)
{
return a*b/gcd(a,b);
}
void pushdown(int no)
{
if (tag[no])
{
tag[no<<1]+=tag[no];
tag[no<<1|1]+=tag[no];
seg[no<<1]=(seg[no<<1]+tag[no])%looplen[no<<1];
seg[no<<1|1]=(seg[no<<1|1]+tag[no])%looplen[no<<1|1];
tag[no]=0;
}
}
void pushup(int no)
{
seg[no]=0;
if (!looplen[no<<1]) seg[no]+=seg[no<<1];
else seg[no]+=loopsum[no<<1][seg[no<<1]];
if (!looplen[no<<1|1]) seg[no]+=seg[no<<1|1];
else seg[no]+=loopsum[no<<1|1][seg[no<<1|1]];
if (looplen[no<<1]&&looplen[no<<1|1])
{
ll nowl=seg[no<<1],nowr=seg[no<<1|1];
if (!looplen[no]) looplen[no]=lcm(looplen[no<<1],looplen[no<<1|1]);
seg[no]=0;
for(int i=0;i<looplen[no];i++)
{
loopsum[no][i]=loopsum[no<<1][nowl]+loopsum[no<<1|1][nowr];
nowl=(nowl+1)%looplen[no<<1];
nowr=(nowr+1)%looplen[no<<1|1];
}
}
}
void check(int no)
{
if (!len[seg[no]])
{
looplen[no]=loop[seg[no]];
for(int i=0;i<looplen[no];i++)
{
loopsum[no][i]=seg[no];
seg[no]=seg[no]*seg[no]%p;
}
seg[no]=0;
}
}
void buildtree(int no,int l,int r)
{
if (l==r)
{
seg[no]=a[l];
check(no);
return;
}
int mid=(l+r)>>1;
buildtree(no<<1,l,mid);
buildtree(no<<1|1,mid+1,r);
pushup(no);
}
void modify(int no,int l,int r,int s,int t)
{
if (l>=s&&r<=t&&looplen[no])
{
tag[no]++;
seg[no]=(seg[no]+1)%looplen[no];
return;
}
if (l==r)
{
seg[no]=seg[no]*seg[no]%p;
check(no);
return;
}
int mid=(l+r)>>1;
pushdown(no);
if (s<=mid) modify(no<<1,l,mid,s,t);
if (t>mid) modify(no<<1|1,mid+1,r,s,t);
pushup(no);
}
ll query(int no,int l,int r,int s,int t)
{
if (l>=s&&r<=t)
{
if (!looplen[no]) return seg[no];
else return loopsum[no][seg[no]];
}
int mid=(l+r)>>1;
ll sum=0;
pushdown(no);
if (s<=mid) sum+=query(no<<1,l,mid,s,t);
if (t>mid) sum+=query(no<<1|1,mid+1,r,s,t);
return sum;
}
int main()
{
scanf("%d%d%lld",&n,&m,&p);
for(ll i=0;i<p;i++)
if (!vis[i])
{
int x=i,top=0;
while(!vis[x])
{
st[++top]=x;
pos[x]=top;
vis[x]=i+1;
x=x*x%p;
}
if (vis[x]!=i+1)
{
for(int j=1;j<=top;j++)
len[st[j]]=top-j+1+len[x];
}
else
{
for(int j=1;j<pos[x];j++)
len[st[j]]=pos[x]-j;
for(int j=pos[x];j<=top;j++)
loop[st[j]]=top-pos[x]+1;
}
}
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
buildtree(1,1,n);
for(int i=1;i<=m;i++)
{
int op,l,r;
scanf("%d%d%d",&op,&l,&r);
if (!op) modify(1,1,n,l,r);
else printf("%lld
",query(1,1,n,l,r));
}
return 0;
}