首先先說明本題的思路。題目要求有三種操做,兩種是不一樣的在線修改,還有一種是在查詢取模後的結果。而這兩種操做又是區間乘法和區間加法,咱們能夠驚喜的發現這兩種操做對於取模運算來講都是自由的!可是面對很是大的數據,咱們必須思考怎麼樣用線段樹優雅的跑過這道題目。ui
面對這兩種操做,能夠聯想到線段樹的一個很是好的功能就是lazytag,只計算出確實須要訪問的區間的真實值,其餘的保存在lazytag裏面,這樣能夠近似O(NlogN)的運行起來。在嘗試着寫了只有一個lazetag的程序以後咱們發現一個lazytag是不可以解決問題的,那就上兩個,分別表示乘法意義上的lazytag和加法意義上的lazytag。緊接着想到pushdown操做以後咱們又發現必須在向下傳遞lazytag的時候人爲地爲這兩個lazytag規定一個前後順序,排列組合一下只有兩種狀況:spa
①加法優先,即規定好segtree[root*2].value=((segtree[root*2].value+segtree[root].add)*segtree[root].mul)%p,問題是這樣的話很是不容易進行更新操做,假如改變一下add的數值,mul也要聯動變成奇奇怪怪的分數小數損失精度,咱們心裏是很拒絕的;code
②乘法優先,即規定好segtree[root*2].value=(segtree[root*2].value*segtree[root].mul+segtree[root].add*(本區間長度))%p,這樣的話假如改變add的數值就只改變add,改變mul的時候把add也對應的乘一下就能夠了,沒有精度損失,看起來很不錯。blog
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 ; }
codeget