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

时间: 2024-10-18 12:05:23

bzoj 1798 双标记区间修改线段树的相关文章

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

题目大意: 维护一个数列,支持区间乘,区间加,求区间和. 线段树题,对于乘和加操作我们可以维护一个标记.对于乘用乘法分配律分解. 代码如下: 1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 const int N = 100010; 7 int inline getint() 8 { 9

[BZOJ 4826]影魔 区间修改主席树 标记永久化

为了这道题还特地去学了标记永久化,可能对于区间修改主席树或者树套树比较有用吧OvO 我们可以把答案分为两部分:p1造成的和p2造成的 我们枚举序列,用单调栈求出序列每一个位置i,左右边第一个比它大的L,R 开三棵主席树tree1 tree2 tree3 把L扔进tree1的R位置(单点+1),L+1~i-1扔进tree2的R位置,i+1~R-1扔进tree3的L位置(区间+1) 然后询问[l,r]的时候,求出三棵区间主席树 p1造成的贡献为区间tree1内大于等于L的个数 p2造成的贡献为区间t

hihoCoder #1078 : 线段树的区间修改(线段树区间更新板子题)

#1078 : 线段树的区间修改 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 对于小Ho表现出的对线段树的理解,小Hi表示挺满意的,但是满意就够了么?于是小Hi将问题改了改,又出给了小Ho: 假设货架上从左到右摆放了N种商品,并且依次标号为1到N,其中标号为i的商品的价格为Pi.小Hi的每次操作分为两种可能,第一种是修改价格——小Hi给出一段区间[L, R]和一个新的价格NewP,所有标号在这段区间中的商品的价格都变成NewP.第二种操作是询问——小Hi给出一段

【BZOJ3110】【整体二分+树状数组区间修改/线段树】K大数查询

Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M 接下来M行,每行形如1 a b c或2 a b c Output 输出每个询问的结果 Sample Input 2 5 1 1 2 1 1 1 2 2 2 1 1 2 2 1 1 1 2 1 2 3 Sample Output 1 2 1 HINT

WHYZOJ-#53 线段树区间修改(线段树)

[题目描述]: 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.求出某区间每一个数的和 [输入描述]: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值. 接下来M行每行包含3或4个整数,表示一个操作,具体如下: 操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k 操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和 [输出描述]: 输出包含若干行整

【USACO 2008 Nov Gold】 3.Light Switching(lites 开关灯) 区间修改线段树

题意: n.m,n个灯,m次操作 两种操作 0: 这段区间全部状态取反,初始全部为0 1: 询问这段区间有几个灯是亮的. 裸线段树,弱爆了. #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 101000 #define inf 0x3f3f3f3f using namespace std; struct Segment_Tree {

BZOJ 3211 花神游历各国 线段树题解

BZOJ 3211 花神游历各国 线段树题解 3211: 花神游历各国 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 2551  Solved: 946[Submit][Status][Discuss] Description Input Output 每次x=1时,每行一个整数,表示这次旅行的开心度 Sample Input 4 1 100 5 5 5 1 1 2 2 1 2 1 1 2 2 2 3 1 1 4 Sample Output 101

【区间合并线段树】BZOJ1593-安排住宿

[题目大意] 查询最左端的连续长度区间:或批量修改一些区间.[思路] 区间合并线段树……复习一下.POJ上有一样的题目,我居然还借用了别人的权限号去做BZOJ,简直愚昧到没朋友[笑cry] 处理方法以前的博文里有,这里有不赘述了. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cstdlib> 6 using

支持区间修改的树状数组

支持区间修改的树状数组 原理 对于一个数组\(a\),以及\(a\)的差分\(c\),显然有\(c[i]=a[i]-a[i-1]\) 那么对于数组a的前缀和有 \(\sum_{i=1}^n{a_i}=c[1]+(c[1]+c[2])+...(c[1]+c[2]+...+c[n])\) 进一步的: \(\sum_{i=1}^n{a_i}=c[1]*n+c[2]*(n-1)+...+c[n]*(n-n+1)\) 展开括号内 \(\sum_{i=1}^n{a_i}=c[1]*n+c[2]*n+...+