https://www.luogu.org/problem/P3373
题目描述
如题,已知一个数列,你需要进行下面三种操作:
1.将某区间每一个数乘上x
2.将某区间每一个数加上x
3.求出某区间每一个数的和
输入格式
第一行包含三个整数N、M、P,分别表示该数列数字的个数、操作的总个数和模数。
第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。
接下来M行每行包含3或4个整数,表示一个操作,具体如下:
操作1: 格式:1 x y k 含义:将区间[x,y]内每个数乘上k
操作2: 格式:2 x y k 含义:将区间[x,y]内每个数加上k
操作3: 格式:3 x y 含义:输出区间[x,y]内每个数的和对P取模所得的结果
输出格式
输出包含若干行整数,即为所有操作3的结果。
输入输出样例
输入 #1复制
5 5 38
1 5 4 2 3
2 1 4 1
3 2 5
1 2 4 2
2 3 5 5
3 1 4
输出 #1复制
17
2
说明/提示
时空限制:1000ms,128M
数据规模:
对于30%的数据:N<=8,M<=10
对于70%的数据:N<=1000,M<=10000
对于100%的数据:N<=100000,M<=100000
(数据已经过加强^_^)
样例说明:
故输出应为17、2(40 mod 38=2)
思路
既有乘法,既有加法,那么设立两个算法标记,但是某个区间的乘法和加法顺序先后不同,得到的答案也会不同,我们先对标记进行一些预处理。如果要给[l,r]区间添加乘法标记flag1,而且[l,r]区间已经有了加法标记flag,那么flag=flag*flag1,对于这部分乘法来说,只针对目前的加法标记flag,后面继续添加的乘法标记针对的已经是新的加法标记flag,他们对子区间的值和乘法标记并没有什么影响。
乘法标记的初始值为1,但是在运算取余的过程中可能变成0,这会出错,把0改为模值就可以了,一直错在这
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#define rep(i,s,e) for(int i=s;i<=e;i++)
#define rep1(i,s,e) for(int i=s;i<e;i++)
#define rep2(i,s,e,c) for(int i=s;i>=e;i--)
#define pfi(x) printf("%d\n",x)
#define pfl(x) printf("%lld\n",x)
#define pfn() printf("\n")
#define pfs() printf(" ")
#define sfi(x) scanf("%d",&x)
#define sfi1(x,y) scanf("%d%d",&x,&y)
#define sff(x) scanf("%lf",&x)
#define sfl(x) scanf("%lld",&x)
#define memset1(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;
const int MAX = 1e5 + 50;
const int mod = 996873654;
ll n,M,p;
ll ar[MAX];
struct node {
ll l,r,w;
ll flag,flag1;
}Ltree[MAX<<2];
void creat(ll l,ll r,ll k){
Ltree[k].l=l;
Ltree[k].r=r;
Ltree[k].flag1=1;
Ltree[k].flag=0;
if(l==r){
Ltree[k].w=ar[l]%p;
return;
}
ll m=(l+r)>>1;
creat(l,m,k<<1);
creat(m+1,r,k<<1|1);
Ltree[k].w=(Ltree[k<<1].w+Ltree[k<<1|1].w)%p;
}
void down(ll k){
Ltree[k<<1].flag=(Ltree[k].flag+Ltree[k<<1].flag*Ltree[k].flag1)%p;
Ltree[k<<1|1].flag=(Ltree[k].flag+Ltree[k<<1|1].flag*Ltree[k].flag1)%p;
Ltree[k<<1].flag1=Ltree[k<<1].flag1*Ltree[k].flag1%p;
Ltree[k<<1|1].flag1=Ltree[k<<1|1].flag1*Ltree[k].flag1%p;
if(!Ltree[k<<1].flag1) Ltree[k<<1].flag1=p;
if(!Ltree[k<<1|1].flag1) Ltree[k<<1|1].flag1=p;
Ltree[k<<1].w=(Ltree[k<<1].w*Ltree[k].flag1+(Ltree[k<<1].r-Ltree[k<<1].l+1)*Ltree[k].flag)%p;
Ltree[k<<1|1].w=(Ltree[k<<1|1].w*Ltree[k].flag1+(Ltree[k<<1|1].r-Ltree[k<<1|1].l+1)*Ltree[k].flag)%p;
Ltree[k].flag=0;
Ltree[k].flag1=1;
}
void updates(ll left,ll right,ll v,ll k,int com){
if(Ltree[k].l>right||Ltree[k].r<left) return;
if(Ltree[k].l>=left&&Ltree[k].r<=right){
if(com==2){
Ltree[k].w+=(Ltree[k].r-Ltree[k].l+1)*v;
Ltree[k].flag=(Ltree[k].flag+v)%p;
}
else{
Ltree[k].w*=v;
Ltree[k].flag1=Ltree[k].flag1*v%p;
Ltree[k].flag=(Ltree[k].flag*v)%p;//如果在此乘法之前,有加法,那么预先处理该部分
}
Ltree[k].w%=p;
return;
}
if(Ltree[k].flag || Ltree[k].flag1>1) down(k);
ll m=(Ltree[k].l+Ltree[k].r)>>1;
if(left>m){
updates(left,right,v,k<<1|1,com);
}
else if(right<=m){
updates(left,right,v,k<<1,com);
}
else{
updates(left,m,v,k<<1,com);
updates(m+1,right,v,k<<1|1,com);
}
Ltree[k].w=(Ltree[k<<1].w+Ltree[k<<1|1].w)%p;
}
ll asksum(ll left,ll right,ll k){
if(Ltree[k].l>right||Ltree[k].r<left) return 0;
if(Ltree[k].l>=left&&Ltree[k].r<=right){
return Ltree[k].w;
}
if(Ltree[k].flag || Ltree[k].flag1>1) down(k);
ll m=(Ltree[k].l+Ltree[k].r)>>1;
ll sum1=0,sum2=0;
if(left>m) sum1=asksum(left,right,k<<1|1);
else if(right<=m) sum2=asksum(left,right,k<<1);
else{
sum1=asksum(left,m,k<<1);
sum2=asksum(m+1,right,k<<1|1);
}
return (sum1+sum2)%p;
}
int main(){
// freopen("testdata.in","r",stdin);
// freopen("1.out","w",stdout);
scanf("%lld%lld%lld",&n,&M,&p);
for(int i=1;i<=n;i++){
sfl(ar[i]);
}
creat(1,n,1);
for(int i=0;i<M;i++){
int opt;
ll x,y;
scanf("%d%lld%lld",&opt,&x,&y);
if(opt==1){
ll v;
sfl(v);
updates(x,y,v,1,1);
}
else if(opt==3){
pfl(asksum(x,y,1));
}
else{
ll v;
sfl(v);
updates(x,y,v,1,2);
}
}
return 0;
}