【模板】线段树 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)

思路:

问题主要是没法分断是先乘还是先加,我同学说可以能个栈存一下,线段树里的栈。。。恕我无能。

一般的解决方法是把乘法变成加法。

(s+flag)*lazy=s*lazy+flag*lazy。

注意:

数据规模,不仅要开long long,还要时常取mod。

(根据评论中的话,好像不开long long也行,但即使数据是int范围内的,因为有乘法操作,可能会动不动就到了long long范围内。noip时,Linux格式化输入输出lld。)

代码实现:

 1 #include<cstdio>
 2 #include<iostream>
 3 using namespace std;
 4 #define LL long long
 5 LL n,m,mod,a,b,c,d;
 6 struct nate{LL l,r,s,flag,lazy;}t[800000];//flag
 7 void heritage(LL k){//标记传递。
 8     if(t[k].lazy==1&&t[k].flag==0) return;
 9     if(t[k].l==t[k].r){t[k].flag=0;t[k].lazy=1;return;}
10     LL lson=k*2,rson=k*2+1;
11     t[lson].s=((t[lson].s*t[k].lazy)%mod+(t[k].flag*(t[lson].r-t[lson].l+1))%mod)%mod;
12     t[lson].flag=((t[lson].flag*t[k].lazy)%mod+t[k].flag)%mod;
13     t[lson].lazy=(t[lson].lazy*t[k].lazy)%mod;
14     t[rson].s=((t[rson].s*t[k].lazy)%mod+(t[k].flag*(t[rson].r-t[rson].l+1))%mod)%mod;
15     t[rson].flag=((t[rson].flag*t[k].lazy)%mod+t[k].flag)%mod;
16     t[rson].lazy=(t[rson].lazy*t[k].lazy)%mod;
17     t[k].flag=0;t[k].lazy=1;
18 }
19 void make_tree(LL k,LL l,LL r){//建树。一般建法。
20     LL lson=k*2,rson=k*2+1;
21     t[k].l=l;t[k].r=r;t[k].lazy=1;
22     if(l==r){scanf("%lld",&t[k].s);t[k].s%=mod;return;}
23     LL mid=(l+r)/2;
24     make_tree(lson,l,mid);
25     make_tree(rson,mid+1,r);
26     t[k].s=(t[lson].s+t[rson].s)%mod;
27 }
28 void interval_add(LL k,LL l,LL r,LL v){//加法更值。
29     LL lson=k*2,rson=k*2+1;
30     heritage(k);
31     if(t[k].l==l&&t[k].r==r){
32         t[k].flag=v;
33         t[k].s=(t[k].s+((t[k].r-t[k].l+1)*v)%mod)%mod;
34         return;
35     }
36     LL mid=(t[k].l+t[k].r)/2;
37     if(l<=mid) interval_add(lson,l,min(r,mid),v);
38     if(r>mid) interval_add(rson,max(l,mid+1),r,v);
39     t[k].s=(t[lson].s+t[rson].s)%mod;
40 }
41 void interval_ride(LL k,LL l,LL r,LL v){//乘法更值。
42     LL lson=k*2,rson=k*2+1;
43     heritage(k);
44     if(t[k].l==l&&t[k].r==r){
45         t[k].lazy=v;
46         t[k].s=(t[k].s*v)%mod;
47         return;
48     }
49     LL mid=(t[k].l+t[k].r)/2;
50     if(l<=mid) interval_ride(lson,l,min(r,mid),v);
51     if(r>mid) interval_ride(rson,max(l,mid+1),r,v);
52     t[k].s=(t[lson].s+t[rson].s)%mod;
53 }
54 LL interval_query(LL k,LL l,LL r){//区间查询。
55     int lson=k*2,rson=k*2+1;
56     heritage(k);
57     if(t[k].l==l&&t[k].r==r) return t[k].s;
58     LL mid=(t[k].l+t[k].r)/2,ans=0;
59     if(l<=mid) ans+=interval_query(lson,l,min(r,mid));
60     if(r>mid) ans+=interval_query(rson,max(l,mid+1),r);
61     return ans%mod;
62 }
63 int main(){
64     scanf("%lld%lld%lld",&n,&m,&mod);
65     make_tree(1,1,n);
66     for(int i=1;i<=m;i++){
67         scanf("%lld",&a);
68         if(a==1){
69             scanf("%lld%lld%lld",&b,&c,&d);
70             interval_ride(1,b,c,d%mod);
71         }
72         if(a==2){
73             scanf("%lld%lld%lld",&b,&c,&d);
74             interval_add(1,b,c,d%mod);
75         }
76         if(a==3){
77             scanf("%lld%lld",&b,&c);
78             printf("%lld\n",interval_query(1,b,c));
79         }
80     }
81     return 0;
82 }

有没有感觉很恶心,慢慢来吧。

时间: 2024-10-25 08:46:11

【模板】线段树 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

【线段树】【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

算法模板——线段树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

模板——线段树

一颗最简单的线段树orz...但是感觉还是拍得好麻烦... 只支持区间加和区间查询 #include<iostream> #include<cstdlib> #include<cstdio> #include<algorithm> using namespace std; int a[2000010],n,m; typedef long long ll; struct inlinetree{ int l,r; ll s,lazy; }lt[34000100]

模板 线段树的区间修改

线段树的区间修改 区间绝对标记 改成同一个数 注意打标记前 要先判断 是否有标记 这道题不能像加法标记一样 标记初始化为 0 如果这道题 可以将数变成 0 那么0 就不能为初始值了 然后我们初始值要选择一个不会被干扰到的数字 比如 -1 就不会变成 -1 另外还要注意在标记清空时 要将标记 变成 -1 而不是 0 1 #include <cstdio> 2 #include <cstdlib> 3 #include <cmath> 4 #include <cstr

算法模板——线段树之Lazy标记

一.前言 前面我们已经知道线段树能够进行单点修改和区间查询操作(基本线段树).那么如果需要修改的是一个区间该怎么办呢?如果是暴力修改到叶子节点,复杂度即为\(O(nlog_n)\),显然是十分不优秀的.那么我们能不能向区间查询一样把复杂度降到\(O(log_n)\)呢? 二.算法流程 线段树肯定是兹瓷\(O(log_n)\)修改的,否则发明它有何用处?所以,我我们现在需要知道,如何快速进行区间修改操作.首先,我们回顾下终止节点 假定我要在这个图上修改区间[2,8],我只要修改掉图上所有的终止节点