题目大意
对于一个序列,进行以下两种操作:1)将区间 [li,ri] 内的数字平方后 mod 2018,2)求不取模的平方和。
解题思路
由于模数很小,所以可以从这里下手。打一个表之后可以发现,每个数都将在进行几次 mod 2018 意义下的平方运算之后进入一个长度很小的循环节,且所有循环节长度的 lcm 为 6。而且可以发现,每个数字经过不超过 5 次运算都可以进入循环节,所以在某个数字进入循环节之前,每次都可以对其进行暴力修改。
对于线段树的每个结点,维护以下几个量:1)sum:区间和;2)cir[]:该结点的循环节内各位表示的数;3)len:该结点的循环节长度;4)pos:该结点当前在循环节的位置;5)in:该结点是否在循环节;6)tag:加标记。
代码
忘记初始化了,比赛时候wa了一发好难受啊。
#include <bits/stdc++.h>
using namespace std;
inline int read() {
register int val=0, sign=1; char ch;
while(~(ch=getchar()) && (ch<'0' || ch>'9') && ch!='-'); ch=='-'?sign=-1:val=ch-'0';
while(~(ch=getchar()) && (ch>='0' && ch<='9')) val=(val<<1)+(val<<3)+ch-'0';
return val*sign;
}
const int maxn=int(1e5)+111, p=2018, maxp=2333;
int n,m;
int a[maxn];
int nxt[maxp], vis[maxp], inc[maxp];
int sum[maxn<<2];
int len[maxn<<2], cir[maxn<<2][10];
int pos[maxn<<2], in[maxn<<2], tag[maxn<<2];
inline int gcd(int a,int b) {return b?gcd(b,a%b):a;}
inline int lcm(int a,int b) {return a*b/gcd(a,b);}
void init(int k,int v) {
sum[k]=v, in[k]=inc[v];
if(in[k]) {
len[k]=1, cir[k][pos[k]=0]=v;
for(int i=nxt[v];i!=v;i=nxt[i])
cir[k][len[k]++]=i;
}
return;
}
#define mid ((l+r)>>1)
#define ls(k) ((k)<<1)
#define rs(k) (ls(k)|1)
void update(int k) {
sum[k]=sum[ls(k)]+sum[rs(k)];
in[k]=in[ls(k)]&in[rs(k)];
if(in[k]) {
len[k]=lcm(len[ls(k)],len[rs(k)]), pos[k]=0;
assert(len[k]);
int tx=pos[ls(k)], ty=pos[rs(k)];
for(int i=0;i<len[k];i++) {
cir[k][i]=cir[ls(k)][tx++]+cir[rs(k)][ty++];
if(tx==len[ls(k)]) tx=0;
if(ty==len[rs(k)]) ty=0;
}
}
return;
}
void add(int k,int i) {
tag[k]+=i, pos[k]+=i;
assert(len[k]);
while(len[k] && pos[k]>=len[k]) pos[k]%=len[k];
sum[k]=cir[k][pos[k]];
}
void pushdown(int k) {
if(!tag[k]) return;
add(ls(k),tag[k]), add(rs(k),tag[k]);
tag[k]=0;
return;
}
void build(int k,int l,int r) {
sum[k]=in[k]=tag[k]=pos[k]=len[k]=0;
if(l==r) {init(k,a[l]); return;}
build(ls(k),l,mid), build(rs(k),mid+1,r);
update(k);
return;
}
void modify(int k,int l,int r,int x,int y) {
if(x<=l && r<=y && in[k]) {add(k,1); return;}
if(l==r) {init(k,nxt[sum[k]]); return;}
pushdown(k);
if(x<=mid) modify(ls(k),l,mid,x,y);
if(y> mid) modify(rs(k),mid+1,r,x,y);
update(k);
return;
}
int query(int k,int l,int r,int x,int y) {
if(x<=l && r<=y) return sum[k];
if(tag[k]) pushdown(k);
int res=0;
if(x<=mid) res+=query(ls(k),l,mid,x,y);
if(y> mid) res+=query(rs(k),mid+1,r,x,y);
return res;
}
void work() {
n=read();
for(int i=1;i<=n;i++)
a[i]=read();
build(1,1,n);
char op[10];
m=read();
while(m--) {
scanf("%s",op);
int l=read(), r=read();
if(op[0]=='Q') printf("%d\n",query(1,1,n,l,r));
else modify(1,1,n,l,r);
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("input0.txt","r",stdin);
freopen("output.txt","w",stdout);
#endif
for(int i=0;i<p;i++) inc[i]=1, nxt[i]=i*i%p;
for(int i=0;i<p;i++) if(!vis[i]) {
int y=i;
while(!vis[y]) vis[y]=1, y=nxt[y];
for(int x=i;x!=y;x=nxt[x]) inc[x]=0;
}
int T=read();
for(int i=1;i<=T;++i) {
printf("Case #%d:\n",i);
work();
}
return 0;
}