线段树

 

首先先说明本题的思路。题目要求有三种操作,两种是不同的在线修改,还有一种是在查询取模后的结果。而这两种操作又是区间乘法和区间加法,我们可以惊喜的发现这两种操作对于取模运算来说都是自由的!但是面对非常大的数据,我们必须思考怎么样用线段树优雅的跑过这道题目。

面对这两种操作,可以联想到线段树的一个非常好的功能就是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 ;
}

code

 

posted @ 2019-03-03 19:43  Isaunoya  阅读(151)  评论(0编辑  收藏  举报
TOP