首先先说明本题的思路。题目要求有三种操作,两种是不同的在线修改,还有一种是在查询取模后的结果。而这两种操作又是区间乘法和区间加法,我们可以惊喜的发现这两种操作对于取模运算来说都是自由的!但是面对非常大的数据,我们必须思考怎么样用线段树优雅的跑过这道题目。
面对这两种操作,可以联想到线段树的一个非常好的功能就是lazytag,只计算出确实需要访问的区间的真实值,其他的保存在lazytag里面,这样可以近似O(NlogN)的运行起来。在尝试着写了只有一个lazetag的程序之后我们发现一个lazytag是不能够解决问题的,那就上两个,分别表示乘法意义上的lazytag和加法意义上的lazytag。紧接着想到pushdown操作之后我们又发现必须在向下传递lazytag的时候人为地为这两个lazytag规定一个先后顺序,排列组合一下只有两种情况:
①加法优先,即规定好segtree[root*2].value=((segtree[root*2].value+segtree[root].add)*segtree[root].mul)%p,问题是这样的话非常不容易进行更新操作,假如改变一下add的数值,mul也要联动变成奇奇怪怪的分数小数损失精度,我们内心是很拒绝的;
②乘法优先,即规定好segtree[root*2].value=(segtree[root*2].value*segtree[root].mul+segtree[root].add*(本区间长度))%p,这样的话假如改变add的数值就只改变add,改变mul的时候把add也对应的乘一下就可以了,没有精度损失,看起来很不错。
inline void build (LL l,LL r,LL rt) {//建树
if (l == r) { sum [rt] = a[l] ; return ; }
LL mid = l + r >> 1;
build ( l , mid , rt<<1 ) ;
build ( mid+1 ,r, rt << 1|1 ) ;
sum [rt] = sum [rt<<1] + sum [rt<<1|1] ;
sum [rt] %= p ; mul [rt] = 1;
}
//核心代码,维护线段树
inline void push_down (LL l,LL r,LL rt){
int mid = l + r >> 1 ;
add [ rt<<1 ] = (add [rt<<1] * mul [rt] + add [rt] ) % p ;
add [ rt<<1|1 ] = (add [rt<<1|1] * mul [rt] + add [rt] ) % p ;
mul [ rt<<1 ] = (mul [rt<<1] * mul [rt] ) % p ;
mul [ rt<<1|1 ] = (mul [rt<<1|1] * mul [rt] ) % p ;
sum [ rt<<1 ] = (sum [rt<<1] * mul [rt] + add [rt] * ( mid - l + 1 ) ) %p ;
sum [ rt<<1|1 ] = (sum [rt<<1|1] * mul [rt] + add [rt] * (r-mid) ) %p ;
add [ rt ] = 0 ; mul [rt] = 1 ;
}
//乘法
inline void longer_write_add (LL a,LL b,LL l,LL r,LL rt,LL j){
if (a<=l and r<=b){
sum[rt] += j*(r-l+1) ; add[rt] += j ; return ;
}
push_down(l, r, rt) ;
LL mid= l + r >> 1;
if ( a <= mid ) longer_write_add(a, b, l, mid, rt<<1, j) ;
if ( b > mid ) longer_write_add(a, b, mid+1, r, rt<<1|1, j) ;
sum [ rt ] = sum [ rt<<1 ] + sum [ rt<<1|1 ] ;
sum [ rt ] %= p ;
}
//同上 加法
inline LL longer_query (LL L,LL R,LL l,LL r,LL rt){
if ( L <= l and r <= R ){ return sum[rt] ;}
push_down(l, r, rt) ;
LL mid = l + r >> 1 ;
LL ans1 , ans2 ; ans1 = ans2 = 0 ;
if(L<=mid) ans1=longer_query(L, R, l, mid, rt<<1);
if(mid<R) ans2=longer_query(L, R, mid+1, r, rt<<1|1);
return ( ans1 + ans2 ) % p ;
}