洛谷 P3373 【模板】线段树 2 题解
题面题目链接:[戳这里](https://www.luogu.org/problemnew/show/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)
题解
看起来不难的样子。。无非就是俩标记,按时取模,然后加的时候加到加法懒标记里,乘的时候加法懒标记和乘法懒标记都乘上。
注意取模!!!注意取模!!!注意取模!!!
代码:
#include<cstdio>
using namespace std
;
typedef long long ll
;
const ll maxn
=100005;
ll a
[maxn
],n
,m
,p
;
struct tree
{
ll l
,r
,val
,laza
,lazm
;
void check(){
val
%=p
,laza
%=p
,lazm
%=p
;
}
}tr
[maxn
*4];
void pushdown(ll id
){
tr
[id
].check();
tr
[id
<<1].val
*=tr
[id
].lazm
;
tr
[id
<<1].val
+=tr
[id
].laza
*(tr
[id
<<1].r
-tr
[id
<<1].l
+1);
tr
[id
<<1].lazm
*=tr
[id
].lazm
;
tr
[id
<<1].laza
=tr
[id
<<1].laza
*tr
[id
].lazm
+tr
[id
].laza
;
tr
[id
<<1|1].val
*=tr
[id
].lazm
;
tr
[id
<<1|1].val
+=tr
[id
].laza
*(tr
[id
<<1|1].r
-tr
[id
<<1|1].l
+1);
tr
[id
<<1|1].lazm
*=tr
[id
].lazm
;
tr
[id
<<1|1].laza
=tr
[id
<<1|1].laza
*tr
[id
].lazm
+tr
[id
].laza
;
tr
[id
].lazm
=1;tr
[id
].laza
=0;
tr
[id
<<1].check();tr
[id
<<1|1].check();
}
#define mu tr[id].val=tr[id<<1].val+tr[id<<1|1].val
void buildtree(ll id
,ll l
,ll r
){
tr
[id
].l
=l
,tr
[id
].r
=r
,tr
[id
].laza
=0,tr
[id
].lazm
=1;
if(l
==r
){tr
[id
].val
=a
[l
];tr
[id
].check();return;}
ll mid
=l
+r
>>1;
buildtree(id
<<1,l
,mid
);
buildtree(id
<<1|1,mid
+1,r
);
mu
;
tr
[id
].check();
}
void treeadd(ll al
,ll ar
,ll val
,ll id
){
ll l
=tr
[id
].l
,r
=tr
[id
].r
,mid
=l
+r
>>1;
if(al
>r
||ar
<l
)return;
if(al
<=l
&&ar
>=r
){
tr
[id
].val
+=(r
-l
+1)*val
;
tr
[id
].laza
+=val
;
tr
[id
].check();
return ;
}
pushdown(id
);
treeadd(al
,ar
,val
,id
<<1);
treeadd(al
,ar
,val
,id
<<1|1);
mu
;
tr
[id
].check();
}
void trmultiply(ll al
,ll ar
,ll val
,ll id
){
ll l
=tr
[id
].l
,r
=tr
[id
].r
,mid
=l
+r
>>1;
if(al
>r
||ar
<l
)return;
if(al
<=l
&&ar
>=r
){
tr
[id
].val
*=val
;
tr
[id
].laza
*=val
;
tr
[id
].lazm
*=val
;
tr
[id
].check();return;
}
pushdown(id
);
trmultiply(al
,ar
,val
,id
<<1);
trmultiply(al
,ar
,val
,id
<<1|1);
mu
;
tr
[id
].check();
}
ll
query(ll id
,ll al
,ll ar
){
ll l
=tr
[id
].l
,r
=tr
[id
].r
,mid
=l
+r
>>1;
if(al
>r
||ar
<l
)return 0;
if(al
<=l
&&ar
>=r
)return tr
[id
].val
;
pushdown(id
);
ll ans
=query(id
<<1,al
,ar
);
ans
+=query(id
<<1|1,al
,ar
);
ans
%=p
;
return ans
;
}
int main(){
scanf("%lld%lld%lld",&n
,&m
,&p
);
for(ll i
=1;i
<=n
;i
++)scanf("%lld",&a
[i
]);buildtree(1,1,n
);
for(ll i
=0;i
<m
;i
++){
ll x
,y
,k
,b
;
scanf("%lld%lld%lld",&b
,&x
,&y
);
if(b
==3){
printf("%lld\n",query(1,x
,y
));
}
else{
scanf("%lld",&k
);
if(b
-1) treeadd(x
,y
,k
,1);
else trmultiply(x
,y
,k
,1);
}
}
return 0;
}
P.S.一开始冗余操作挺多的,某些玄学的客观因素加成下超时了一个点T_T。。再次提交同一份代码卡时通过,删去一部分可以确定无用的取模后,用时440ms。。。应该还有一些取模是不必要的,不过懒得删了,手动滑稽