#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxlen=100000+10;
int n,m;
ll size,he[maxlen],cheng[maxlen],jia[maxlen],k,ans,p,a[maxlen];
int kuai[maxlen];
int l[maxlen],r[maxlen];//存每个块的左右边界
void pushdown(int x){
for(int i=l[x];i<=r[x];i++)//把这个块里面的都pushdown
a[i]=(a[i]*cheng[x]+jia[x])%p;//!!!!!先乘后加!!!!!!!
cheng[x]=1;
jia[x]=0;
}
int main(){
cin>>n>>p;
size=sqrt(n);
for(int i=1;i<=n;i++) kuai[i]=(i-1)/size+1;
for(int i=n;i>=1;i--) l[kuai[i]]=i;//计算块的左边界
//本来是左边的 但是后来被右边的盖着了 为了让他可以被盖着 所以从后向前
for(int i=1;i<=n;i++) r[kuai[i]]=i;//计算块的右边界
for(int i=1;i<=n;i++) cin>>a[i];
cin>>m;
for(int i=1;i<=n;i++) he[kuai[i]]+=a[i];
for(int i=1;i<=n;i++) cheng[kuai[i]]=1;
for(int i=1;i<=m;i++){
int opt,x,y;
cin>>opt>>x>>y;
if(opt==1){//乘法
cin>>k;
pushdown(kuai[x]);//加之前和乘之前都需要把delta清零
int temp=min(y,r[kuai[x]]);//算出左侧碎块右边界
for(int j=x;j<=temp;j++){//左侧碎块
he[kuai[x]]+=(k-1)*a[j]%p;
a[j]=(a[j]*k)%p;
}
pushdown(kuai[y]);//重置右侧碎块
for(int j=y;j>=l[kuai[y]];j--){//将右侧碎块算出
he[kuai[y]]+=(k-1)*a[j]%p;
a[j]=a[j]*k%p;
}
if(kuai[x]!=kuai[y]){//中间还有完整的块
for(int j=kuai[x]+1;j<=kuai[y]-1;j++){//整块做加法,乘法标记,算出和
cheng[j]=cheng[j]*k%p;
jia[j]=(jia[j]*k)%p;//存delta
he[j]=he[j]*k%p;//算和
}
}
}
if(opt==2){//加法
cin>>k;
int t=min(y,r[kuai[x]]);//算出左侧碎块右边界
pushdown(kuai[x]);//加之前和乘之前都需要把delta清零
he[kuai[x]]=(he[kuai[x]]+(t-x+1)*k)%p;//和值增加元素数*k
for(int j=x;j<=t;j++) a[j]=(a[j]+k)%p;//算出左侧碎块每个数
pushdown(kuai[y]);//重置加、乘标记,算出值
he[kuai[y]]=(he[kuai[y]]+((y-l[kuai[y]]+1)*k))%p;//和值增加元素数*k
for(int j=y;j>=l[kuai[y]];j--) a[j]=(a[j]+k)%p;
if(kuai[x]!=kuai[y]){//中间还有完整块
for(int j=kuai[x]+1;j<=kuai[y]-1;j++){//整块
jia[j]+=k;//加法标记+k
he[j]=(he[j]+size*k)%p;//和值增加每块元素数*k
}
}
}
if(opt==3){//查询 但是不pushdown 省时间只算需要的
ans=0;
int t=min(y,r[kuai[x]]);//左侧最碎块边界
for(int j=x;j<=t;j++){
ans+=(a[j]*cheng[kuai[x]]+jia[kuai[x]])%p;//
ans%=p;
}
for(int j=y;j>=l[kuai[y]];j--){
ans+=(a[j]*cheng[kuai[y]]+jia[kuai[y]])%p;
ans%=p;
}
if(kuai[x]!=kuai[y]){
for(int j=kuai[x]+1;j<=kuai[y]-1;j++){//完整块的值
ans+=he[j];
ans%=p;
}
}
cout<<ans%p<<endl;
}
}
return 0;
}
这是用分块写的 然后还用分块写了一个线段树2 模板 几乎跟着提一模一样 P3373
#include<bits/stdc++.h>
using namespace std;
long long size,n,m,opt,he[100100],cheng[100100],jia[100100],k,p,a[100100];
long long kuai[100100],l[100100],r[100100],x,y;
void pushdown(long long x){
for(long long i=l[x];i<=r[x];i++){
a[i]=(a[i]*cheng[x]+jia[x])%p;
}
cheng[x]=1;
jia[x]=0;
}
void init(){
cin>>n>>m>>p;
size=sqrt(n);
for(long long i=1;i<=n;i++){
kuai[i]=(i-1)/size + 1;
}
for(long long i=n;i>=1;i--){
l[kuai[i]]=i;
}
for(long long i=1;i<=n;i++){
r[kuai[i]]=i;
}
for(long long i=1;i<=n;i++) cin>>a[i];
for(long long i=1;i<=n;i++) he[kuai[i]]+=a[i];
for(long long i=1;i<=n;i++) cheng[kuai[i]]=1;
}
void work(){
for(long long i=1;i<=m;i++){
cin>>opt>>x>>y;
if(opt==1){//乘法
//左边碎块pushdown 右边碎块pushdown 中间cheng数组变化就可以了
cin>>k;
long long temp=min(y,r[kuai[x]]);//左块右边界
pushdown(kuai[x]);
for(long long j=x;j<=temp;j++){
he[kuai[x]]+=((k-1)*a[j])%p;
a[j]=(a[j]*k)%p;
}
pushdown(kuai[y]);
for(long long j=y;j>=l[kuai[y]];j--){
he[kuai[y]]+=((k-1)*a[j])%p;
a[j]=(a[j]*k)%p;
}
if(kuai[x]!=kuai[y]){
for(long long j=kuai[x]+1;j<=kuai[y]-1;j++){
cheng[j]=cheng[j]*k%p;
jia[j]=(jia[j]*k)%p;
he[j]=(he[j]*k)%p;
}
}
}
else if (opt==2){
cin>>k;
long long t=min(y,r[kuai[x]]);
pushdown(kuai[x]);
he[kuai[x]]=(he[kuai[x]]+(t-x+1)*k)%p;
for(long long j=x;j<=t;j++){
a[j]=(a[j]+k)%p;
}
pushdown(kuai[y]);
he[kuai[y]]=(he[kuai[y]]+((y-l[kuai[y]]+1)*k))%p;
for(long long j=y;j>=l[kuai[y]];j--){
a[j]=(a[j]+k)%p;
}
if(kuai[x]!=kuai[y]){
for(long long j=kuai[x]+1;j<=kuai[y]-1;j++){
jia[j]+=k;
he[j]=(he[j]+size*k)%p;
}
}
}
else if (opt==3){
long long ans=0;
long long t=min(y,r[kuai[x]]);
for(long long j=x;j<=t;j++){
ans+=(a[j]*cheng[kuai[x]]+jia[kuai[x]])%p;
}
for(long long j=y;j>=l[kuai[y]];j--){
ans+=(a[j]*cheng[kuai[y]]+jia[kuai[y]])%p;
}
if(kuai[x]!=kuai[y]){
for(long long j=kuai[x]+1;j<=kuai[y]-1;j++){
ans+=he[j];
}
}
ans%=p;
cout<<ans<<endl;
}
}
}
int main(){
init();
work();
return 0;
}