当我刚刚接触线段树区间乘法时,因为一直没想懂乘法和加法的懒标记怎么一起下推,上百度和CSDN搜索程序讲解,但搜到的绝大多数都是一些没有讲解,直挂代码的博文,很难让人看懂代码意思,往往会花费大量时间在理解变量的作用和操作的原理,因此为了方便初学者学习(也为了方便我以后复习),就写了这篇博文,希望对你有所帮助!
题目:
代码解释:
代码虽然长,但原理简单
#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
ll a[100001],tree[400001],mark[400001],mark_[400001],n,m,M; //a用来存原数组的值,tree用来维护线段树,mark用来记录加法的懒标记
//mark_用来记录乘法的懒标记,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+