如题,已知一个数列,你需要进行下面三种操作:
- 将某区间每一个数乘上x
- 将某区间每一个数加上x
- 求出某区间每一个数的和对p取模的结果
很明显这又是一个RMQ的题目,可以用线段树求解。
(以下均假设区间长度为len,原来的和为x)
前提:很明显,如果某一个区间同时加上一个数a,那么整个区间的和变为x+len*a;如果同时乘a,那么和就变为ax。
然而本题有两种修改操作,使用1种懒标记显然是不够的。所以用2种,分别为加法标记addt,乘法标记mult。在有两种标记时,由于标记要下放,所以
必须人为规定两种标记的更新顺序。
比如,对某个区间先进行+add1,再 *mul,再+add2,其和变为
m
u
l
(
x
+
a
d
d
1
)
+
a
d
d
2
mul(x+add1)+add2
mul(x+add1)+add2,我们记它为true_res
如果认为加法优先,那么和就变为
m
u
l
(
x
+
a
d
d
1
+
a
d
d
2
)
mul(x+add1+add2)
mul(x+add1+add2)
想让它变为true_res,做法就是令add2/mul。
进一步得到:在某次乘法之后进行的加法,要先把加上的数除以乘过的值mult(不知道这样理解对不对)。然而这很可能会导致除出浮点数。由于计算机中浮点数存储存在误差,所以此做法不可行。
如果认为乘法优先,那么和就变为
x
∗
m
u
l
+
a
d
d
1
+
a
d
d
2
x*mul+add1+add2
x∗mul+add1+add2
想让它变为true_res,做法是让先加的 add1乘上mul。
进一步得到以下结论:在某次乘法之前进行的加法,在进行当这次乘法后必须乘上要乘的值,而在之后进行的加法,则不需要这一操作。
这样,在下放懒标记时,可以让
- 左右儿子的值:先乘 mult[fa],再加 len * addt[fa]
- 左右儿子的addt:先乘 mul[fa],再加 addt[fa]
- 左右儿子的mult:由于乘法有优先性,故直接乘mul[fa]即可
代码如下:
struct seg_tree {
long data;
long addt,mult;
inline seg_tree():data(0),addt(0),mult(1) {}
};
seg_tree a[M*4];
int n,p;
inline void set_tag(int son,int f,int child_len) {
a[son].data=(a[son].data*a[f].mult+child_len*a[f].addt)%p;
a[son].addt=(a[son].addt*a[f].mult+a[f].addt)%p; //?
a[son].mult=(a[son].mult*a[f].mult)%p;
}
inline void push_down(int k,int l,int r) {
int lc=k*2+1,rc=k*2+2;
int child_len=(r-l+1)/2;
set_tag(lc,k,child_len);
set_tag(rc,k,child_len);
a[k].addt=0;
a[k].mult=1;
}
void add(int st,int end,long val,int k=0,int l=0,int r=n) {
if(l>end||r<st) return;
if(l>=st&&r<=end) {
a[k].addt=(a[k].addt+val)%p;
a[k].data=(a[k].data+(r-l+1)*val)%p;
return;
}
push_down(k,l,r);
int mid=(l+r)/2,lc=k*2+1,rc=k*2+2;
add(st,end,val,lc,l,mid);
add(st,end,val,rc,mid+1,r);
a[k].data=(a[lc].data+a[rc].data)%p;
}
void mul(int st,int end,long val,int k=0,int l=0,int r=n) {
if(l>end||r<st) return;
if(l>=st&&r<=end) {
a[k].mult=(a[k].mult*val)%p;
//注意:这一步必须有,因为在本次乘法之前进行的加法,在进行当本次乘法后必须乘上要乘的值
a[k].addt=(a[k].addt*val)%p;
//区间内所有数*k,则整个区间的和*k
a[k].data=(a[k].data*val)%p;
return;
}
push_down(k,l,r);
int mid=(l+r)/2,lc=k*2+1,rc=k*2+2;
mul(st,end,val,lc,l,mid);
mul(st,end,val,rc,mid+1,r);
a[k].data=(a[lc].data+a[rc].data)%p;
}
long query(int st,int end,int k=0,int l=0,int r=n) {
if(l>end||r<st) return 0;
if(l>=st&&r<=end) return a[k].data;
push_down(k,l,r);
int lc=k*2+1,rc=k*2+2;
long L=query(st,end,lc,l,(l+r)/2);
long R=query(st,end,rc,1+(l+r)/2,r);
return (L+R)%p;
}