bzoj 1798 双标记区间修改线段树
Posted zzq
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 1798 双标记区间修改线段树相关的知识,希望对你有一定的参考价值。
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
Submit: 6680 Solved: 2377
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
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
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
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 双标记区间修改线段树的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ 1798 AHOI2009 Seq 维护序列 线段树
[bzoj 1798][luogu p2023]Seq 线段树Seq