线段树的进阶使用(洛谷3373 )

题目描述

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

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按照题目模拟即可,写乘法运算的时候请记得注意以下的小细节:1.乘法的数组的初始值要设为1,不然系统默认为0做乘法的时候是没有意义的~2.要注意位运算的使用,在别代码中,二进制的迅速是无法替代的3.加法和乘法的运算顺序要把握好,乘在先,加在后4.PushDown函数的加法和乘法需要写在一起,不然前者会把后者的覆盖住,这样就会对后面的产生干扰5.养成好习惯,做一步,取模一步前车之鉴,后车之师,下面放上代码,当当当~~(在这里感谢大佬封癫(U25815 )以及 yql的信徒的热切帮助~)
  1 #include<bits/stdc++.h>
  2 #define maxn 1000007
  3 #define LL long long
  4 using namespace std;
  5 LL n,m;
  6 LL c[4*maxn],a[maxn];
  7 LL add[maxn];
  8 LL P;
  9 LL addplus[maxn];
 10 void PushUp(LL rt)
 11 {
 12      c[rt]=c[rt<<1]+c[rt<<1|1];
 13      c[rt]%=P;
 14 }
 15 void Build(LL l,LL r,LL rt)
 16 {
 17      if(l==r)
 18      {
 19          c[rt]=a[l];
 20          return ;
 21      }
 22      LL m=(l+r)>>1;
 23      Build(l,m,rt<<1);
 24      Build(m+1,r,rt<<1|1);
 25      PushUp(rt);
 26 }
 27 void Pushdown(LL rt,LL ln,LL rn)
 28 {
 29    if((addplus[rt]==1)&&(!add[rt]))return ;//这里要用"&&",如果满足一种情况也是要进行考虑的
 30
 31        addplus[rt<<1]=(addplus[rt<<1]*addplus[rt])%P;
 32        addplus[rt<<1|1]=(addplus[rt<<1|1]*addplus[rt])%P;
 33        c[rt<<1]=(c[rt<<1]*addplus[rt])%P;
 34        c[rt<<1|1]=(c[rt<<1|1]*addplus[rt])%P;
 35
 36       add[rt<<1]=(add[rt<<1]*addplus[rt]+add[rt])%P;
 37       add[rt<<1|1]=(add[rt<<1|1]*addplus[rt]+add[rt])%P;
 38       c[rt<<1]=(c[rt<<1]+add[rt]*ln)%P;
 39       c[rt<<1|1]=(c[rt<<1|1]+add[rt]*rn)%P;
 40
 41       add[rt]=0;
 42       addplus[rt]=1;
 43 }
 44 void Update_interval(LL L,LL R,LL C,LL l,LL r,LL  rt)
 45 {
 46     if(L<=l&&r<=R)
 47     {
 48       c[rt]+=C*(r-l+1);
 49       c[rt]%=P;
 50       add[rt]+=C;
 51       add[rt]%=P;
 52       return ;
 53    }
 54    LL m=(l+r)>>1;
 55    Pushdown(rt,m-l+1,r-m);
 56    if(L<=m) Update_interval(L,R,C,l,m,rt<<1);
 57    if(R>m) Update_interval(L,R,C,m+1,r,rt<<1|1);
 58    PushUp(rt);
 59    return ;
 60 }
 61 LL Query(LL L,LL R,LL l,LL r,LL rt)
 62 {
 63        if(L<=l&&r<=R)
 64           return c[rt];
 65       LL m=(l+r)>>1;
 66       LL ans=0;
 67       Pushdown(rt,m-l+1,r-m);
 68       if(L<=m) ans+=Query(L,R,l,m,rt<<1);
 69       if(R>m) ans+=Query(L,R,m+1,r,rt<<1|1);
 70       PushUp(rt);
 71       ans%=P;
 72       return ans;
 73 }
 74 void Update_intervalplus(LL L,LL R,LL C,LL l,LL r,LL rt)
 75 {
 76    if(L<=l&&r<=R)
 77     {
 78       c[rt]*=C;
 79       c[rt]%=P;
 80       add[rt]*=C;
 81       add[rt]%=P;
 82       addplus[rt]*=C;
 83       addplus[rt]%=P;
 84       return ;
 85    }
 86    LL m=(l+r)>>1;
 87    Pushdown(rt,m-l+1,r-m);
 88    if(L<=m) Update_intervalplus(L,R,C,l,m,rt<<1);
 89    if(R>m) Update_intervalplus(L,R,C,m+1,r,rt<<1|1);
 90    PushUp(rt);
 91    return ;
 92 }
 93
 94 int main()
 95 {
 96     ios::sync_with_stdio(false);//这句话一定要加上,不然c++的输入输出太慢了。
 97     cin>>n>>m;
 98     for(int i=0;i<=maxn;i++)
 99       addplus[i]=1;
100     cin>>P;
101     for(LL i=1;i<=n;i++)
102       cin>>a[i];
103       Build(1,n,1);
104       LL t1,t2,t3,t4;
105     for(LL  i=1;i<=m;i++)
106     {
107          cin>>t1;
108          if(t1==1)
109          {
110             cin>>t2>>t3>>t4;
111             Update_intervalplus(t2,t3,t4,1,n,1);
112          }
113          if(t1==2)
114           {
115                   cin>>t2>>t3>>t4;
116                   Update_interval(t2,t3,t4,1,n,1);
117           }
118           if(t1==3)
119           {
120              cin>>t2>>t3;
121              cout<<(Query(t2,t3,1,n,1))%P<<endl;
122           }
123     }
124     return 0;
125 }
				
时间: 2024-10-06 07:42:05

线段树的进阶使用(洛谷3373 )的相关文章

二分查找or线段树(借教室洛谷1083vijos1782NOIP 2012 提高组 第二天 第二题)

在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样. 面对海量租借教室的信息,我们自然希望编程解决这个问题.我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借.共有m份订单,每份订单用三个正整数描述,分别为dj,sj,tj,表示某租借者需要从第sj天到第tj天租借教室(包括第sj天和第tj天),每天需要租借dj个教室. 我们假定,租借者对教室的大小.地点没有要求.即对于每份

[洛谷3373]【模板】线段树 2

思路: 线段树.同时维护两个 lazy tag ,一个维护乘,一个维护加.根据加法结合律,可以得出:当同一个结点进行两次加操作时,新的标记等于两次标记之和.根据乘法结合律,可以得出:当同一个结点进行两次乘操作时,新的标记等于两次标记之积.根据乘法分配律,可以得出:当同一个结点先进行了加操作,再进行乘操作时,两个标记都要乘以新乘上的值. 1 #include<cstdio> 2 #include<cctype> 3 #include<cstring> 4 #include

洛谷 3373 【模板】线段树 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]内

洛谷P3372 【模板】线段树 1

P3372 [模板]线段树 1 153通过 525提交 题目提供者HansBug 标签 难度普及+/提高 提交  讨论  题解 最新讨论 [模板]线段树1(AAAAAAAAA- [模板]线段树1 洛谷评测机出问题了吗? 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.求出某区间每一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值. 接

洛谷P1531 I Hate It 线段树

洛谷P1531 I Hate It 线段树 单点修改 区间求最大 1 #include <cstdio> 2 #include <cmath> 3 #include <cstdlib> 4 #include <cstring> 5 #include <string> 6 #include <algorithm> 7 #include <iomanip> 8 #include <iostream> 9 using

洛谷1558 色板游戏 线段树

我先立个Flag 我,这几天,要过1W道线段树题. 题目背景 阿宝上学了,今天老师拿来了一块很长的涂色板. 题目描述 色板长度为L,L是一个正整数,所以我们可以均匀地将它划分成L块1厘米长的小方格.并从左到右标记为1, 2, ... L.现在色板上只有一个颜色,老师告诉阿宝在色板上只能做两件事:1. "C A B C" 指在A到 B 号方格中涂上颜色 C.2. "P A B" 指老师的提问:A到 B号方格中有几种颜色.学校的颜料盒中一共有 T 种颜料.为简便起见,我

洛谷P3434 [POI2006]KRA-The Disks(线段树)

洛谷题目传送门 \(O(n)\)的正解算法对我这个小蒟蒻真的还有点思维难度.洛谷题解里都讲得很好. 考试的时候一看到300000就直接去想各种带log的做法了,反正不怕T...... 我永远只会有最直观的思路(最差的程序效率) 题目相当于每次让我们找区间\([1,las-1]\)中上数第一个比当前盘子半径小的位置(las为上一次盘子掉到的位置)于是用线段树无脑搞一下,维护区间最小值,每次找这个位置,能往左跳就往左,不能的话再往右,当然如果超过了las-1就不用找了,直接放在las上面(相当于la

[洛谷P1198/BZOJ1012][JSOI2008] 最大数 - 树状数组/线段树?

其实已经学了树状数组和线段树,然而懒得做题,所以至今没写多少博客 Description 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值. 限制:L不超过当前数列的长度.(L>=0) 2. 插入操作. 语法:A n 功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列的末尾. 限制:n是整数(可能为负数)并且在长整

洛谷P3380 【模板】二逼平衡树(树套树,树状数组,线段树)

洛谷题目传送门 emm...题目名写了个平衡树,但是这道题的理论复杂度最优解应该还是树状数组套值域线段树吧. 就像dynamic ranking那样(蒟蒻的Sol,放一个link骗访问量233) 所有的值(包括初始a数组,操作1.3.4.5的k)全部先丢进去离散化 对于1操作查比它小的数,挑log棵线段树,找区间小于这个数的个数+1,这个还比较好像 操作2就是dynamic ranking,log棵线段树一起加减,像静态主席树求第k小一样跳,操作3 dynamic ranking里也有 操作4先