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

转载于:https://www.cnblogs.com/zzqc/p/7279002.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值