如題,已知乙個數列,你需要進行下面三種操作:
1.將某區間每乙個數乘上x
2.將某區間每乙個數加上x
3.求出某區間每乙個數的和
輸入格式:
第一行包含三個整數n、m、p,分別表示該數列數字的個數、操作的總個數和模數。
第二行包含n個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。
接下來m行每行包含3或4個整數,表示乙個操作,具體如下:
操作1: 格式:1 x y k 含義:將區間[x,y]內每個數乘上k
操作2: 格式:2 x y k 含義:將區間[x,y]內每個數加上k
操作3: 格式:3 x y 含義:輸出區間[x,y]內每個數的和對p取模所得的結果
輸出格式:
輸出包含若干行整數,即為所有操作3的結果。
線段樹維護區間加、區間乘。
相比最普通的線段樹,我們這裡在每乙個節點都存兩個標記:
mark[ x ][ 0 ](加標記)表示對於節點x所表示的一整段區間,每個數通過加法操作所增加的值(就是說先加a再乘b再加c之後,這裡存的數是a*b+c)
mark[ x ][ 1 ](乘標記)表示對於節點x所表示的一整段區間,每個數被乘了幾次。
p[ x ]表示的一整段區間中每乙個數的和是多少。
接下來考慮各種操作,假設我們要對[l,r]的每乙個數進行操作,當前正在處理編號為x的[l,r]的區間(這裡保證了不存在r
如果進行加操作,當l<=l,r<=r,即當前區間被完整覆蓋,我們將mark[ x ][ 0 ]直接加上加的數,再更新一下p[ x ]的值。若當前區間不被完整覆蓋,就pushdown一下,再處理遞迴子節點就好。
如果進行乘操作,當l<=l,r<=r,即當前區間被完整覆蓋,我們將mark[ x ][ 1 ]與mark[ x ][ 0 ]都
直接乘上乘的數,更新一下p[ x ]的值(因為從這之前這個區間每乙個數通過加法操作所增加的數都被乘了)。若當前區間不被完整覆蓋,同樣的,就pushdown一下,再處理遞迴子節點就好。
接下來就是較為複雜的pushdown了:
首先pushdown的一大意義在於,乙個區間上的標記表示的是區間內每乙個數都具備的特點,當我們需要pushdown時,就說明我們對區間內部子區間進行操作,導致區間的標記不再具有普遍性,我們需要把標記向下傳遞,並把當前區間的標記清空。
所以pushdown分為以下幾個部分:
1、將左右兒子區間的乘標記都乘上本區間的乘標記。
2、將左右兒子區間的加標記都先乘上本區間的乘標記,再加上本區間的加標記。
3、將左右兒子的區間和的值先乘上本區間乘標記,再分加上本區間加標記與左右兒子區間長度的乘積。
4、將本區間的標記清空,再在對左右兒子操作完成後用左右兒子的區間和更新本區間的區間和。
然後就是**了。
#include#include#include#include#include#define ll long long#define m 500000
#define mid (l+r>>1)
using namespace std;
ll read()
ll n,mod,q,p[m],mark[m][2],a[m],l,r,qs,num;
void build(ll x,ll l,ll r)
build(x<<1,l,mid),build(x<<1|1,mid+1,r);
p[x]=(p[x<<1]+p[x<<1|1])%mod;
}void up(ll x)
void pushdown(ll x,ll l,ll r)
void add(ll x,ll l,ll r,ll m){
if(r
洛谷 P3373 線段樹 2
作為一道調了三天的模板題,真的太虐心了對於理解線段樹大有用處。傳送門如題,已知乙個數列,你需要進行下面三種操作 1.將某區間每乙個數乘上x 2.將某區間每乙個數加上x 3.求出某區間每乙個數的和 輸入格式 第一行包含三個整數n m p,分別表示該數列數字的個數 操作的總個數和模數。第二行包含n個用空...
線段樹2 洛谷p3373 線段樹
題目位址 解釋 多了乙個乘法操作,可以考慮優先順序。每次先算乘法。首先,對於乙個區間 和為s 假設已經按 a 乘b進行了操作。值得到的值為 s a b sb ab 假設先乘得到 sb a 這樣相比,add應該還要再乘上乙個b才對,所以,當更新到乙個區間時,為了進行先乘的操作而不讓結果發生變化,應該將...
洛谷P3373 模板 線段樹2
這題有毒啊,敲了我一晚上加一早上,總算a了。由於有加和乘兩個操作,要用2個lazy陣列。核心難點就是2個lazy陣列會相互影響。因為乘影響加,加不影響乘,所以我們先算乘。include include include include include include include include i...