当我刚刚接触线段树区间乘法时,因为一直没想懂乘法和加法的懒标记怎么一起下推,上百度和CSDN搜索程序讲解,但搜到的绝大多数都是一些没有讲解,直挂代码的博文,很难让人看懂代码意思,往往会花费大量时间在理解变量的作用和操作的原理,因此为了方便初学者学习(也为了方便我以后复习),就写了这篇博文,希望对你有所帮助!
题目:
代码解释:
代码虽然长,但原理简单
#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
ll a[100001],tree[400001],mark[400001],mark_[400001],n,m,M;
inline void read(ll &a) {
ll x(0),y(1);char c=getchar();
while(c<'0'||c>'9') {
if(c=='-')y=-1;c=getchar();
}
while(c>='0'&&c<='9') {
x=(x<<1)+(x<<3)+(c^48);c=getchar();
}
a=x*y;return;
}
inline void write(ll x) {
if(x<0) {
putchar('-');x=-x;write(x);
} else {
if(x>9)
write(x/10);
putchar(x%10+'0');
}
}
push_dawn函数:
push_dawn函数是程序的核心,主要作用是把加法和乘法标记下推,并维护左右子树的值,下面会对此函数进行详细解释
第一个部分:左右子树数值维护
首先,因为子树同时有乘法的标记和加法的标记,我们需要一起处理
先说乘法:
因为乘法是把从
l
l
l 到
r
r
r 的所有数乘以
k
k
k 所以可以得到如下公式:
a
l
+
a
l
+
1
+
.
.
.
+
a
r
=
a
l
×
k
+
a
l
+
1
×
k
+
.
.
.
+
a
r
×
k
a_l+a_{l+1}+...+a_r=a_l×k+a_{l+1}×k+...+a_r×k
al?+al+1?+...+ar?=al?×k+al+1?×k+...+ar?×k
提取公因式:
a
l
×
k
+
a
l
+
1
×
k
+
.
.
.
+
a
r
×
k
=
k
×
(
a
l
+
a
l
+
1
+
.
.
.
+
a
r
)
a_l×k+a_{l+1}×k+...+a_r×k=k×(a_l+a_{l+1}+...+a_r)
al?×k+al+1?×k+...+ar?×k=k×(al?+al+1?+...+ar?)
又因为
t
r
e
e
tree
tree 数组·刚好就是维护区间和的,所以可以得到:
k
×
(
a
l
+
a
l
+
1
+
.
.
.
+
a
r
)
=
k
×
t
r
e
e
l
~
r
k×(a_l+a_{l+1}+...+a_r)=k×tree_{l\sim r}
k×(al?+al+1?+...+ar?)=k×treel~r?
所以我们想要知道
t
r
e
e
tree
tree 数组在处理完乘法标记后的数值时,就可以直接用标记乘以
t
r
e
e
tree
tree
再说加法:
因为加法是把从
l
l
l 到
r
r
r 的所有数加上
k
k
k 所以可以得到如下公式:
a
l
+
a
l
+
1
+
.
.
.
+
a
r
=
a
l
+
k
+
a
l
+
1
+
k
+
.
.
.
+
a
r
+
k
a_l+a_{l+1}+...+a_r=a_l+k+a_{l+1}+k+...+a_r+k
al?+al+1?+...+ar?=al?+k+al+1?+k+...+ar?+k
提取公因式:
a
l
+
k
+
a
l
+
1
+
k
+
.
.
.
+
a
r
+
k
=
(
r
?
l
+
1
)
×
k
+
(
a
l
+
a
l
+
1
+
.
.
.
+
a
r
)
a_l+k+a_{l+1}+k+...+a_r+k=(r-l+1)×k+(a_l+a_{l+1}+...+a_r)
al?+k+al+1?+k+...+ar?+k=(r?l+1)×k+(al?+al+1?+...+ar?)
又因为
t
r
e
e
tree
tree 数组·刚好就是维护区间和的,所以可以得到:
(
r
?
l
+
1
)
×
k
+
(
a
l
+
a
l
+
1
+
.
.
.
+
a
r
)
=
(
r
?
l
+
1
)
×
k
+
t
r
e
e
l
~
r
(r-l+1)×k+(a_l+a_{l+1}+...+a_r)=(r-l+1)×k+tree_{l\sim r}
(r?l+1)×k+(al?+al+1?+...+ar?)=(r?l+1)×k+treel~r?
所以我们想要知道
t
r
e
e
tree
tree 数组在处理完加法标记后的数值时,就可以直接用标记乘以区间内数字个数在加上
t
r
e
e
tree
tree
最后合并
知道了乘法和加法分别怎么处理,最后就很简单了,因为只要把乘法的修改和加法的修改加在一起,就是最后的答案。
也就是
t
r
e
e
p
×
2
=
(
m
a
r
k
_
p
?
t
r
e
e
p
×
2
+
(
l
e
n
?
l
e
n
÷
2
)
×
m
a
r
k
p
)
%
M
tree_{p×2}=(mark\__p*tree_{p×2}+(len-len÷2)×mark_p)\%M
treep×2?=(mark_p??treep×2?+(len?len÷2)×markp?)%M 和
t
r
e
e
p
×
2
+
1
=
(
m
a
r
k
_
p
?
t
r
e
e
p
×
2
+
1
+
(
l
e
n
?
l
e
n
÷
2
)
×
m
a
r
k
p
)
%
M
tree_{p×2+1}=(mark\__p*tree_{p×2+1}+(len-len÷2)×mark_p)\%M
treep×2+1?=(mark_p??treep×2+1?+(len?len÷2)×markp?)%M
void push_down(ll p,ll len) {
tree[p*2]=(mark_[p]*tree[p*2]+(len-len/2)*mark[p])%M;
tree[p*2+1]=(mark_[p]*tree[p*2+1]+(len/2)*mark[p])%M;
mark_[p*2]=mark_[p*2]*mark_[p]%M;
mark_[p*2+1]=mark_[p*2+1]*mark_[p]%M;
mark[p*2]=(mark[p*2]*mark_[p]+mark[p])%M;
mark[p*2+1]=(mark[p*2+1]*mark_[p]+mark[p])%M;
mark[p]=0;
mark_[p]=1;
}
void build(ll l,ll r,ll p) {
mark_[p]=1;
if(l==r) {
tree[p]=a[l];
return;
}
ll mid=(l+r)>>1;
build(l,mid,p*2);
build(mid+1,r,p*2+1);
tree[p]=tree[p*2]+tree[p*2+1];
return;
}
void update(ll l,ll r,ll k,ll p,ll cl,ll cr) {
if(cl>r||cr<l) {
return;
} else {
if(l<=cl&&r>=cr) {
tree[p]+=k*(cr-cl+1);
if(cl<cr) {
mark[p]+=k;
}
} else {
ll mid=(cl+cr)/2;
push_down(p,cr-cl+1);
update(l,r,k,p*2,cl,mid);
update(l,r,k,p*2+1,mid+1,cr);
tree[p]=tree[p*2]+tree[p*2+1];
}
}
}
void update_(ll l,ll r,ll k,ll p,ll cl,ll cr) {
if(cl>r||cr<l) {
return;
} else {
if(l<=cl&&r>=cr) {
tree[p]=tree[p]*k%M;
if(cl<cr) {
mark[p]=mark[p]*k%M;
mark_[p]=mark_[p]*k%M;
}
} else {
ll mid=(cl+cr)/2;
push_down(p,cr-cl+1);
update_(l,r,k,p*2,cl,mid);
update_(l,r,k,p*2+1,mid+1,cr);
tree[p]=tree[p*2]+tree[p*2+1];
}
}
}
ll query(ll l,ll r,ll p,ll cl,ll cr) {
if(cl>r||cr<l) {
return 0;
} else {
if(cl>=l&&cr<=r) {
return tree[p];
} else {
ll mid=(cl+cr)/2;
push_down(p,cr-cl+1);
return query(l,r,p*2,cl,mid)+query(l,r,p*2+1,mid+1,cr);
}
}
}
int main() {
int i;
read(n);read(m);read(M);
for(i=1; i<=n; ++i) {read(a[i]);}
build(1,n,1);
ll q,x,y,k;
for(i=1; i<=m; ++i) {
read(q);read(x);read(y);
if(q==2) {
read(k);
update(x,y,k,1,1,n);
} else {
if(q==3) {
write(query(x,y,1,1,n)%M);
putchar('\n');
} else {
read(k);
update_(x,y,k,1,1,n);
}
}
}
}
如果有帮助就点个赞吧,谢谢啦~·
|