1. 程式人生 > >bzoj 1798 雙標記區間修改線段樹

bzoj 1798 雙標記區間修改線段樹

scrip namespace c++ define sample -c bit 變化 sea

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define MAXN 100000
 4 #define M ((L+R)>>1)
 5 #define lc (id<<1)
 6 #define rc (id<<1|1)
 7 #define LL long long
 8 LL C[(MAXN<<2)+15];
 9 LL P,laz1[(MAXN<<2)+15],laz2[(MAXN<<2)+15];
10 void build(int L,int R,int
id) 11 { 12 laz1[id]=1; 13 laz2[id]=0; 14 if(L==R) {scanf("%lld",&C[id]);C[id]%=P;return;} 15 build(L,M,lc); 16 build(M+1,R,rc); 17 C[id]=(C[lc]+C[rc])%P; 18 } 19 void pushdown(int L,int R,int id) 20 { 21 if(laz1[id]==1&&laz2[id]==0) return; 22 laz1[lc]=laz1[lc]*laz1[id]%P;
23 laz1[rc]=laz1[rc]*laz1[id]%P; 24 laz2[lc]=(laz2[lc]*laz1[id]%P+laz2[id])%P; 25 laz2[rc]=(laz2[rc]*laz1[id]%P+laz2[id])%P; 26 C[lc]=(C[lc]*laz1[id]%P+(M+1-L)*laz2[id]%P)%P; 27 C[rc]=(C[rc]*laz1[id]%P+((LL)R-M)*laz2[id]%P)%P; 28 laz1[id]=1; 29 laz2[id]=0; 30 } 31 void update1(int
L,int R,int id,int l,int r,int x) 32 { 33 if(L>=l&&R<=r) 34 { 35 laz1[id]=laz1[id]*x%P; 36 laz2[id]=laz2[id]*x%P; //id當前擁有的標記已經計算過 37 C[id]=(C[id]*x)%P; 38 return; 39 } 40 pushdown(L,R,id); 41 if(l<=M) update1(L,M,lc,l,r,x); 42 if(r>M) update1(M+1,R,rc,l,r,x); 43 C[id]=(C[lc]+C[rc])%P; 44 } 45 void update2(int L,int R,int id,int l,int r,int x) 46 { 47 if(L>=l&&R<=r) 48 { 49 laz2[id]=(laz2[id]+x)%P; 50 C[id]=(C[id]+(LL)(R+1-L)*x%P)%P; 51 return; 52 } 53 pushdown(L,R,id); 54 if(l<=M) update2(L,M,lc,l,r,x); 55 if(r>M) update2(M+1,R,rc,l,r,x); 56 C[id]=(C[lc]+C[rc])%P; 57 } 58 LL ask(int L,int R,int id,int l,int r) 59 { 60 if(L>=l&&R<=r) {return C[id]%P;} 61 pushdown(L,R,id); 62 LL tmp=0; 63 if(l<=M) tmp=(tmp+ ask(L,M,lc,l,r))%P; 64 if(r>M) tmp=(tmp+ ask(M+1,R,rc,l,r))%P; 65 C[id]=(C[lc]+C[rc])%P; 66 return tmp%P; 67 } 68 int main() 69 { 70 int T,N,m,i,j,k,s; 71 int x,l,r,c; 72 scanf("%d%lld",&N,&P); 73 build(1,N,1); 74 scanf("%d",&m); 75 while(m--) 76 { 77 scanf("%d",&x); 78 if(x==1){ 79 scanf("%d%d%d",&l,&r,&c); 80 update1(1,N,1,l,r,c); 81 } 82 else if(x==2){ 83 scanf("%d%d%d",&l,&r,&c); 84 update2(1,N,1,l,r,c); 85 } 86 else{ 87 scanf("%d%d",&l,&r); 88 printf("%lld\n",ask(1,N,1,l,r)); 89 } 90 } 91

1798: [Ahoi2009]Seq 維護序列seq
Time Limit: 30 Sec Memory Limit: 64 MB
Submit: 6680 Solved: 2377
[Submit][Status][Discuss]

Description

老師交給小可可一個維護數列的任務,現在小可可希望你來幫他完成。 有長為N的數列,不妨設為a1,a2,…,aN 。有如下三種操作形式: (1)把數列中的一段數全部乘一個值; (2)把數列中的一段數全部加一個值; (3)詢問數列中的一段數的和,由於答案可能很大,你只需輸出這個數模P的值。

Input

第一行兩個整數N和P(1≤P≤1000000000)。第二行含有N個非負整數,從左到右依次為a1,a2,…,aN, (0≤ai≤1000000000,1≤i≤N)。第三行有一個整數M,表示操作總數。從第四行開始每行描述一個操作,輸入的操作有以下三種形式: 操作1:“1 t g c”(不含雙引號)。表示把所有滿足t≤i≤g的ai改為ai×c (1≤t≤g≤N,0≤c≤1000000000)。 操作2:“2 t g c”(不含雙引號)。表示把所有滿足t≤i≤g的ai改為ai+c (1≤t≤g≤N,0≤c≤1000000000)。 操作3:“3 t g”(不含雙引號)。詢問所有滿足t≤i≤g的ai的和模P的值 (1≤t≤g≤N)。 同一行相鄰兩數之間用一個空格隔開,每行開頭和末尾沒有多余空格。

Output

對每個操作3,按照它在輸入中出現的順序,依次輸出一行一個整數表示詢問結果。

Sample Input

7 43
1 2 3 4 5 6 7
5
1 2 5 5
3 2 4
2 3 7 9
3 1 3
3 4 7

Sample Output

2
35
8

HINT

【樣例說明】

初始時數列為(1,2,3,4,5,6,7)。
經過第1次操作後,數列為(1,10,15,20,25,6,7)。
對第2次操作,和為10+15+20=45,模43的結果是2。
經過第3次操作後,數列為(1,10,24,29,34,15,16}
對第4次操作,和為1+10+24=35,模43的結果是35。
對第5次操作,和為29+34+15+16=94,模43的結果是8。



測試數據規模如下表所示

數據編號 1 2 3 4 5 6 7 8 9 10
N= 10 1000 1000 10000 60000 70000 80000 90000 100000 100000
M= 10 1000 1000 10000 60000 70000 80000 90000 100000 100000

Source

Day1

d
對於標記數組的理解還是不清楚導致WA數發,如果這個結點標記數組有值說明這些操作已經對這個結點做過了,只是留下標記方便下傳而已,不需要再次用這個標記

操作這個結點。還有就是這個題目有加法和乘法標記兩種,如果不考慮先後次序直接累加在原先的標記上是錯誤的做法,然而我就是這樣...... po

我們定義當前結點的標記 mul=x,add=y 表示在這個節點的每個值都需要乘上x再加上y,所以初始時所有的結點 mul=1,add=0

對於pushdown數組如果這個結點的標記向下傳遞,假設子節點的標記為 mul=x1 add=y1. 傳遞之後mul和add怎麽累加在一起表示呢,

對於元素a相當於 ((a*x1+y1)*x+y)=a*x*x1+y1*x+y,顯然 mul=x*x1 add=y1*x+y; //因為x1,y1是先前存在的標記所以先操作x1,y1

要明確這兩個結點的標記累加在一起是為了傳遞給子節點的子節點,因為上面說過有標記的點自然已經被標記改變過了,對於傳遞下來的標記改變節點的值這點很容易做到 只要 sum=(sum*x+len*add) 即可。

我們要明確標記的作用是對接受標記的節點依據標記值做出想造成的變化,兩層標記加在一起要不影響下面接受時的變化,

對於上面的a(接受標記方)來說就是要mul=x*x1 add=y1*x+yl

說的有些語無倫次了。。。

[S

bzoj 1798 雙標記區間修改線段樹