题目
数据范围如上,两种操作,一种区间立方和,一种区间求和对99971取模
思路来源
https://blog.csdn.net/qq_31759205/article/details/79520745
题解
思维新奇.jpg
打表,发现这玩意有循环节,我怎么可能知道
对任意数x,x*x*x%99971在48次时出现循环节
于是,线段树多开一维,同时维护当前这个区间如果是第0到47次被立方时的答案
类似标记永久化(?)/树上前缀和的东西,也不需要下放标记,
每次修改的时候找到完整区间,区间+1次%48
每次查询的时候,树上前缀和求当前区间已经经历了多少次被立方,输出对应的和
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
const int N=1e5+10,mod=99971;
int t,n,q,a[N],op,l,r;
struct segtree{
int n;
struct node{int l,r,add;ll v[49];}e[N<<2];
#define l(p) e[p].l
#define r(p) e[p].r
#define a(p) e[p].add
#define v(p,i) e[p].v[i]
void up(int p){rep(i,0,47){v(p,i)=(v(p<<1,(i+a(p<<1))%48)+v(p<<1|1,(i+a(p<<1|1))%48))%mod;}}
ll cal(ll x){return x*x*x%mod;}
void bld(int p,int l,int r){
a(p)=0;
l(p)=l;r(p)=r;
if(l==r){v(p,0)=a[l]%mod;rep(i,1,47)v(p,i)=cal(v(p,i-1));return;}
int mid=l+r>>1;
bld(p<<1,l,mid);bld(p<<1|1,mid+1,r);
up(p);
}
void init(int _n){n=_n;bld(1,1,n);}
void chg(int p,int ql,int qr){
if(ql<=l(p)&&r(p)<=qr){a(p)=(a(p)+1)%48;return;}
int mid=l(p)+r(p)>>1;
if(ql<=mid)chg(p<<1,ql,qr);
if(qr>mid)chg(p<<1|1,ql,qr);
up(p);
}
ll sum(int p,int ql,int qr,int x){
x=(x+a(p))%48;
if(ql<=l(p)&&r(p)<=qr)return v(p,x);
int mid=l(p)+r(p)>>1;ll res=0;
if(ql<=mid)(res+=sum(p<<1,ql,qr,x))%=mod;
if(qr>mid)(res+=sum(p<<1|1,ql,qr,x))%=mod;
return res;
}
}seg;
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&q);
rep(i,1,n){
scanf("%d",&a[i]);
}
seg.init(n);
rep(i,1,q){
scanf("%d%d%d",&op,&l,&r);
if(op==1){
seg.chg(1,l,r);
}
else{
printf("%lld\n",seg.sum(1,l,r,0));
}
}
}
return 0;
}