线段树进阶
模板二相对于模板一,加了个区间乘,于是在模板一的基础上需要多开个数组(记录乘法懒标记)、多写个函数(区间乘),还有要把懒标记下放函数做些修改。
结构体内的变量定义:
sum[]:线段树节点对应区间的元素总和;
addv[]:线段树节点对应区间的所有元素待加的值(懒标记)。
mulv[]:线段树节点对应区间的所有元素待乘的值(懒标记)。
struct node
{
ll sum;
ll addv;
ll mulv;
}t[maxn<<2];
过程说明:
- 建树(Build):不再累述,但是要把addv[]初值全部设为0,mulv[]初值全部设为1。
void build(ll k,ll l,ll r)
{
t[k].addv=0;
t[k].mulv=1;
if(l==r)
{
scanf("%lld",&t[k].sum);
return;
}
ll mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid + 1,r);
update(k);
}
- update更新,加个取模就OK
void update(ll k)
{
t[k].sum = (t[k<<1].sum + t[k<<1|1].sum) % p;
}
- 懒标记下放(Pushdown):
原理解释
1.当对某区间执行加法操作时,由于加法优先级低,不会对乘法操作产生影响,故直接相加即可;
2.当对某区间执行乘法操作时,由于乘法优先级高,会对之前的加法操作产生影响,故需要在相乘时不仅对sum和mulv相乘,也需要对addv相乘;
3.由于上述原因,故需要先算乘法再算加法。
void pushdown(ll k,ll l,ll r)
{
ll mid=(l+r)>>1;
t[k<<1].sum =(t[k<<1].sum * t[k].mulv + t[k].addv * (mid - l + 1)) % p;
t[k<<1|1].sum = (t[k<<1|1].sum * t[k].mulv + t[k].addv * (r- (mid + 1) + 1)) % p;
//儿子的值=儿子的值*爸爸的乘法lazytag+爸爸的加法lazytag*儿子的区间长度
t[k<<1].mulv = (t[k<<1].mulv * t[k].mulv) % p;
t[k<<1|1].mulv = (t[k<<1|1].mulv * t[k].mulv) % p;
t[k<<1].addv = (t[k<<1].addv * t[k].mulv + t[k].addv) % p;
t[k<<1|1].addv = (t[k<<1|1].addv * t[k].mulv +t[k].addv) % p;//维护lazytag
t[k].addv = 0;
t[k].mulv = 1;//父节点初始化
}
细节实现:
1.子树的sum、mulv、addv值分别乘上当前节点的mulv值;
2.子树的addv值加上当前节点的addv值;
3.子树的sum值加上(子树包含元素数量*当前节点的addv值);
4.当前节点的addv值还原,即置为0 ;
5.当前节点的mulv值还原,即置为1;
特别说明:
1.当前节点的懒标是否需要判断?其实判不判都没影响,判断了那么就会快一点,如果为空则不需执行此下放函数。虽然执行了也不会有太大影响(好像前面乘一个常数2?)
2.为尽量节省时间,要将判断放在此函数外而不是函数内,就是pushdown函数的外面判断
- 区间乘(Mul):
void mul(ll k,ll l,ll r,ll x,ll y,ll v)
{
if(l>=x && r<=y)
{
t[k].sum = (t[k].sum * v) % p;
t[k].mulv = (t[k].mulv * v) % p;
t[k].addv = (t[k].addv * v) % p;
return;
}
if(x>r || y<l ) return;
if(t[k].mulv!=1 || t[k].addv ) pushdown(k,l,r);
ll mid=(l+r)>>1;
mul(k<<1,l,mid,x,y,v);
mul(k<<1|1,mid+1,r,x,y,v);
update(k);
}
若当前节点完全包含在待更新区间内,则直接修改当前节点的mulv、addv、sum值
- 区间加(Add):跟乘一样
void add(ll k,ll l,ll r,ll x,ll y,ll v)
{
if(l>=x && r<=y)
{
t[k].addv = (t[k].addv + v) % p;
t[k].sum = (t[k].sum+(r-l+1)*v) % p;
return;
}
if(x>r || y<l) return;
if(t[k].mulv!=1 || t[k].addv ) pushdown(k,l,r);
ll mid=(l+r)>>1;
add(k<<1,l,mid,x,y,v);
add(k<<1|1,mid+1,r,x,y,v);
update(k);
}
- 区间查询(Query):同模板一。。。
ll query(ll k,ll l,ll r,ll x,ll y)
{
ll ans=0;
if(l>=x && r<=y) return t[k].sum;
if(t[k].mulv!=1 || t[k].addv ) pushdown(k,l,r);
ll mid = (l + r) >> 1;
if(x<=mid) ans+=query(k<<1,l,mid,x,y);
if(mid<y) ans+=query(k<<1|1,mid + 1,r,x,y);
return ans % p;
}