luogu P3373 【模板】线段树 2

题目描述

如题,已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数加上x

2.将某区间每一个数乘上x

3.求出某区间每一个数的和

输入输出格式

输入格式:

第一行包含三个整数N、M、P,分别表示该数列数字的个数、操作的总个数和模数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含3或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数乘上k

操作2: 格式:2 x y k 含义:将区间[x,y]内每个数加上k

操作3: 格式:3 x y 含义:输出区间[x,y]内每个数的和对P取模所得的结果


输出格式:

输出包含若干行整数,即为所有操作3的结果。

输入输出样例

输入样例#1:

5 5 38
1 5 4 2 3
2 1 4 1
3 2 5
1 2 4 2
2 3 5 5
3 1 4

输出样例#1:

17
2

说明

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=8,M<=10

对于70%的数据:N<=1000,M<=10000

对于100%的数据:N<=100000,M<=100000

(数据已经过加强^_^)

样例说明:

故输出应为17、2(40 mod 38=2)



需要越来越习惯lazy-tag的使用才行啊

tag1表示乘,tag2表示加

线段树的写法真是各有各的风格    大雾

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 using namespace std;
  5
  6 typedef long long ll;
  7
  8 inline int read(){
  9     char ch;
 10     int re=0;
 11     bool flag=0;
 12     while((ch=getchar())!=‘-‘&&(ch<‘0‘||ch>‘9‘));
 13     ch==‘-‘?flag=1:re=ch-‘0‘;
 14     while((ch=getchar())>=‘0‘&&ch<=‘9‘)  re=re*10+ch-‘0‘;
 15     return flag?-re:re;
 16 }
 17
 18 struct segment{
 19     int l,r;
 20     ll sum,tag1,tag2;
 21     segment(){
 22         sum=0;
 23         tag1=1;
 24         tag2=0;
 25     }
 26 };
 27
 28 const int maxn=100001;
 29
 30 int cnt,n,m;
 31 ll mod;
 32 segment tre[maxn<<2];
 33 int data[maxn];
 34
 35 inline void push_up(int x){
 36     tre[x].sum=(tre[x<<1].sum+tre[x<<1|1].sum)%mod;
 37 }
 38
 39 void build(int x,int l,int r){
 40     tre[x].l=l;  tre[x].r=r;
 41     if(l==r){
 42         tre[x].sum=data[l];
 43         return;
 44     }
 45     int mid=(l+r)>>1;
 46     build(x<<1,l,mid);  build(x<<1|1,mid+1,r);
 47     push_up(x);
 48 }
 49
 50 void init(){
 51     n=read();  m=read();  mod=read();
 52     for(int i=1;i<=n;i++)  data[i]=read();
 53     build(1,1,n);
 54 }
 55
 56 inline void push_down(int x){
 57     int lson=x<<1,rson=lson|1;
 58
 59     if(tre[x].tag1!=1){
 60         tre[lson].tag1=(tre[lson].tag1*tre[x].tag1)%mod;
 61         tre[rson].tag1=(tre[rson].tag1*tre[x].tag1)%mod;
 62         tre[lson].tag2=(tre[lson].tag2*tre[x].tag1)%mod;
 63         tre[rson].tag2=(tre[rson].tag2*tre[x].tag1)%mod;
 64         tre[lson].sum=(tre[lson].sum*tre[x].tag1)%mod;
 65         tre[rson].sum=(tre[rson].sum*tre[x].tag1)%mod;
 66         tre[x].tag1=1;
 67     }
 68
 69     if(tre[x].tag2){
 70         tre[lson].tag2=(tre[lson].tag2+tre[x].tag2)%mod;
 71         tre[lson].sum=(tre[lson].sum+tre[x].tag2*(tre[lson].r-tre[lson].l+1))%mod;
 72         tre[rson].tag2=(tre[rson].tag2+tre[x].tag2)%mod;
 73         tre[rson].sum=(tre[rson].sum+tre[x].tag2*(tre[rson].r-tre[rson].l+1))%mod;
 74         tre[x].tag2=0;
 75     }
 76 }
 77
 78 void update_add(int x,int L,int R,int c){
 79     if(L<=tre[x].l&&tre[x].r<=R){
 80         tre[x].tag2=(tre[x].tag2+c)%mod;
 81         tre[x].sum=(tre[x].sum+c*(tre[x].r-tre[x].l+1))%mod;
 82         return;
 83     }
 84
 85     int mid=(tre[x].l+tre[x].r)>>1;
 86     if(tre[x].tag1!=1||tre[x].tag2)  push_down(x);
 87     if(R<=mid)  update_add(x<<1,L,R,c);
 88     else  if(L>mid)  update_add(x<<1|1,L,R,c);
 89     else{  update_add(x<<1,L,mid,c);  update_add(x<<1|1,mid+1,R,c);  }
 90     push_up(x);
 91 }
 92
 93 void update_mul(int x,int L,int R,int c){
 94     if(L<=tre[x].l&&tre[x].r<=R){
 95         tre[x].tag1=(tre[x].tag1*c)%mod;
 96         tre[x].tag2=(tre[x].tag2*c)%mod;
 97         tre[x].sum=(tre[x].sum*c)%mod;
 98         return;
 99     }
100
101     int mid=(tre[x].l+tre[x].r)>>1;
102     if(tre[x].tag1!=1||tre[x].tag2)  push_down(x);
103     if(R<=mid)  update_mul(x<<1,L,R,c);
104     else  if(L>mid)  update_mul(x<<1|1,L,R,c);
105     else{  update_mul(x<<1,L,mid,c);  update_mul(x<<1|1,mid+1,R,c);  }
106     push_up(x);
107 }
108
109 ll query_sum(int x,int L,int R){
110     if(L<=tre[x].l&&tre[x].r<=R){
111         return tre[x].sum;
112     }
113     if(tre[x].tag1!=1||tre[x].tag2)  push_down(x);
114     int mid=(tre[x].l+tre[x].r)>>1;
115     if(R<=mid)  return query_sum(x<<1,L,R);
116     if(L>mid)  return query_sum(x<<1|1,L,R);
117     return (query_sum(x<<1,L,mid)+query_sum(x<<1|1,mid+1,R))%mod;
118 }
119
120 void solve(){
121     int opt,L,R,c;
122     for(int i=0;i<m;i++){
123         opt=read();
124         switch(opt){
125             case 1:{
126                 L=read();  R=read();  c=read();
127                 update_mul(1,L,R,c);
128                 break;
129             }
130             case 2:{
131                 L=read();  R=read();  c=read();
132                 update_add(1,L,R,c);
133                 break;
134             }
135             case 3:{
136                 L=read();  R=read();
137                 printf("%lld\n",query_sum(1,L,R));
138                 break;
139             }
140         }
141     }
142 }
143
144 int main(){
145     //freopen("temp.in","r",stdin);
146     init();
147     solve();
148     return 0;
149 }


她的话不多但笑起来是那么平静优雅

她柔弱的眼神里装的是什么 是思念的忧伤

时间: 2024-09-30 16:13:27

luogu P3373 【模板】线段树 2的相关文章

算法模板——线段树1(区间加法+区间求和)

实现功能——1:区间加法:2:区间求和 最基础最经典的线段树模板.由于这里面操作无顺序之分,所以不需要向下pushup,直接累积即可 1 var 2 i,j,k,l,m,n,a1,a2,a3,a4:longint; 3 a,b:array[0..100000] of longint; 4 function max(x,y:longint):longint;inline; 5 begin 6 if x>y then max:=x else max:=y; 7 end; 8 function min

算法模板——线段树5(区间开根+区间求和)

实现功能——1:区间开根:2:区间求和(此模板以BZOJ3038为例) 作为一个非常规的线段树操作,其tag也比较特殊呵呵哒 1 var 2 i,j,k,l,m,n:longint; 3 a,b:array[0..500000] of int64; 4 function max(x,y:longint):longint;inline; 5 begin 6 if x>y then max:=x else max:=y; 7 end; 8 function min(x,y:longint):long

[模板]线段树1

https://www.luogu.org/problemnew/show/P3372 1 #include <bits/stdc++.h> 2 #define lowbit(a) (a&-a) 3 4 using namespace std; 5 typedef long long LL; 6 7 const LL N = 100010; 8 LL n, m, q, c[N], c2[N], opt, x, y, k; 9 inline LL Sum(LL *a, LL x){ 10

[Luogu] 可持久化线段树 1(主席树)

https://www.luogu.org/problemnew/show/P3834 #include<cstdio> #include<iostream> #include<algorithm> #include<cstring> using namespace std; const int maxn = 2e5 + 10; #define RR freopen("gg.in", "r", stdin) int n

【线段树】【P3372】模板-线段树

百度百科 Definition&Solution 线段树是一种log级别的树形结构,可以处理区间修改以及区间查询问题.期望情况下,复杂度为O(nlogn). 核心思想见百度百科,线段树即将每个线段分成左右两个线段做左右子树.一个线段没有子树,当且仅当线段表示的区间为[a,a]. 由于编号为k的节点的子节点为2k以及2k+1,线段树可以快速的递归左右叶节点. lazy标记:当进行区间修改的时候,如果一个区间整体全部被包含于要修改的区间,则可以将该区间的值修改后,将lazy标记打在区间上,不再递归左

CSU-ACM集训-模板-线段树进阶

A题 原CF 438D The Child and Sequence 题意 给一串数字,m次操作,1.区间查询:2.区间取模:3.单点修改 基本思路 考虑到模如果大于区间的最大值,则取模没有意义.若小于则向下查询并修改,考虑到一个数每次取模最多为原数的\(1/2\),则可认为修改次数不超过\(\log{2}{n}\) 时间复杂度为\(O(n\log{2}{n}\log{2}{n})\) #include<bits/stdc++.h> #define FOR(i,a,b) for(int i=a

模板 - 线段树

线段树还需要模板的菜鸡 #include<bits/stdc++.h> using namespace std; typedef long long ll; #define lt ls, l, m #define rt rs, m + 1, r #define ls (o<<1) #define rs (o<<1|1) const int MAXM = 100000 + 5; ll a[MAXM]; ll st[MAXM * 4], lazy[MAXM * 4]; in

luogu 1712 区间(线段树+尺取法)

题意:给出n个区间,求选择一些区间,使得一个点被覆盖的次数超过m次,最小的花费.花费指的是选择的区间中最大长度减去最小长度. 坐标值这么大,n比较小,显然需要离散化,需要一个技巧,把区间转化为半开半闭区间,然后线段树的每一个节点表示一个半开半闭区间. 接着我们注意到需要求最小的花费,且这个花费只与选择的区间集合中的最大长度和最小长度有关. 这意味着如果最大长度和最小长度一定,我们显然是需要把中间长度的区间尽量的选择进去使答案不会变的更劣. 不妨把区间按长度排序,枚举每个最小长度区间,然后最大区间

算法模板——线段树4(区间加+区间乘+区间覆盖值+区间求和)

实现功能——1:区间加法 2:区间乘法 3:区间覆盖值 4:区间求和 这是个四种常见线段树功能的集合版哦...么么哒(其实只要协调好三种tag的关系并不算太难——前提是想明白了线段树的工作模式) 代码长度几经修改后也大为缩水 还有!!!——通过BZOJ1798反复的尝试,我的出来一个重要结论——尽量减少pushup操作的不必要使用次数,对于程序提速有明显的效果!!! 1 type vet=record 2 a0,a1:longint; 3 end; 4 var 5 i,j,k,l,m,n,a1,

算法模板——线段树7(骰子翻转问题)

实现功能:首先输入一个长度为N的序列,由1-4组成(1表示向前滚一下,2表示向后滚一下,3表示向左滚一下,4表示向右滚一下,骰子原始状态:上1前2左4右5后3下6),然后输入任意多个操作,输入“1 x y”表示将序列第x个数改成y,输入“2 x y”表示输出对于原始状态的骰子,按照从x到y的序列操作可以使骰子变成什么样子 原理:还是线段树,而且只需要点修改区间访问,不过这里面区间之间的合并不再是简单的累加了,而是——置换关系,通过置换关系的合并实现及时的维护即可 1 type 2 cube=ar