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
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 双标记区间修改线段树的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 维护序列seq(双标记线段树)

BZOJ 1798 AHOI2009 Seq 维护序列 线段树

线段树中区间加减和区间覆盖的双标记问题

[bzoj 1798][luogu p2023]Seq 线段树Seq

bzoj 1798 [Ahoi2009]Seq 维护序列seq(线段树+传标)

bzoj1798: [Ahoi2009]Seq 维护序列seq(线段树)