A Simple Problem with Integers
如果能发现对每个数多次操作后结果会存在循环节,就可以知道线段树该维护什么了。
void init() {
for(int i = 1; i < 2018; i++) {
int p = i;
for(int j = 0; j < 15; j++) {
cc[i][j] = p;
p = p*p%2018;
}
}
}
循环节并不是从开始就存在的,可能从第四位开始也可能从第三位开始,然后循环节的长度为6.
我们对于每次更新先暴力更新叶子节点,当叶子节点进入循环节之后我们就标记一下。如果一个区间的左右区间都被标记,也就是说对左右区间我们已经知道循环节了,我们可以合并两个循环节成一个大区间的循环节。
然后我们就可以快速查找区间信息了。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
int Case = 1;
int n, m;
struct node{
int l, r;
int mid() {
return (l+r)/2;
}
ll flag, pos, lz, sum;
int val[7];
}tr[maxn<<2];
int cc[3000][20];
void init() {
for(int i = 1; i < 2018; i++) {
int p = i;
for(int j = 0; j < 15; j++) {
cc[i][j] = p;
p = p*p%2018;
}
}
}
int num[maxn];
void pushup(int rt) {
if(tr[rt<<1].flag && tr[rt<<1|1].flag) {
tr[rt].flag = 1;
tr[rt].pos = 0;
for(int i = 0; i < 6; i++) {
tr[rt].val[i] = tr[rt<<1].val[(tr[rt<<1].pos+i)%6] + tr[rt<<1|1].val[(tr[rt<<1|1].pos+i)%6];
}
}
tr[rt].sum = tr[rt<<1].sum + tr[rt<<1|1].sum;
}
void build(int rt, int l, int r) {
tr[rt].l = l;tr[rt].r = r;
tr[rt].flag = tr[rt].pos = tr[rt].lz = tr[rt].sum = 0;
if(l == r) {
scanf("%lld", &tr[rt].sum);
for(int i = 0; i < 6; i++) {
tr[rt].val[i] = cc[tr[rt].sum][i+4];
}
return;
}
int mid = tr[rt].mid();
build(rt<<1, l, mid);
build(rt<<1|1, mid+1, r);
pushup(rt);
}
void pushdown(int rt) {
if(tr[rt].lz) {
ll &x = tr[rt].lz;
tr[rt<<1].lz = (tr[rt<<1].lz+x)%6;
tr[rt<<1|1].lz = (tr[rt<<1|1].lz+x)%6;
tr[rt<<1].sum = tr[rt<<1].val[tr[rt<<1].pos = (tr[rt<<1].pos+x)%6];
tr[rt<<1|1].sum = tr[rt<<1|1].val[tr[rt<<1|1].pos = (tr[rt<<1|1].pos+x)%6];
x = 0;
}
}
void update(int rt, int l, int r) {
if(tr[rt].l == l && r == tr[rt].r) {
if(tr[rt].flag) {
tr[rt].lz = (tr[rt].lz+1)%6;
tr[rt].sum = tr[rt].val[tr[rt].pos = (tr[rt].pos+1)%6];
return;
}
if(l == r) {
num[l]++;
tr[rt].sum = tr[rt].sum*tr[rt].sum%2018;
if(num[l] == 4) tr[rt].flag = 1;
return;
}
}
pushdown(rt);
int mid = tr[rt].mid();
if(r <= mid) update(rt<<1, l, r);
else if(l > mid)update(rt<<1|1, l, r);
else update(rt<<1, l, mid), update(rt<<1|1, mid+1, r);
pushup(rt);
}
ll query(int rt, int l, int r) {
if(tr[rt].l == l && tr[rt].r == r) {
return tr[rt].sum;
}
pushdown(rt);
int mid = tr[rt].mid();
if(r <= mid) return query(rt<<1, l, r);
else if(l > mid) return query(rt<<1|1, l, r);
else return query(rt<<1, l, mid) + query(rt<<1|1, mid+1, r);
}
void solve() {
static int T = 0;
scanf("%d", &n);
for(int i = 1; i <= n; i++) num[i] = 0;
build(1, 1, n);
scanf("%d", &m);
printf("Case #%d:\n", ++T);
for(int i = 1; i <= m; i++) {
char op[2];int l, r;scanf("%s%d%d", op, &l, &r);
if(op[0] == 'C') update(1, l, r);
else printf("%lld\n", query(1, l, r));
}
return;
}
int main() {
init();
scanf("%d", &Case);
while(Case--) {
solve();
}
return 0;
}