P3373 【模板】线段树 2

相对于线段树模板1有了区间乘的操作,所以增加了一个乘的延迟标记,当加和乘的次序不同的时候,我们要好好考虑这两个延迟标记对孩子的影响。所以这里我们是采取的是先乘后加。

线段树里面也要分清楚是延迟标记依赖就更新自己的值还是把延迟标记传递给孩子的时候才更新自己的值。

题目描述

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

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)

首先先说明本题的思路。题目要求有三种操作,两种是不同的在线修改,还有一种是在查询取模后的结果。而这两种操作又是区间乘法和区间加法,我们可以惊喜的发现这两种操作对于取模运算来说都是自由的!但是面对非常大的数据,我们必须思考怎么样用线段树优雅的跑过这道题目。

面对这两种操作,可以联想到线段树的一个非常好的功能就是lazytag,只计算出确实需要访问的区间的真实值,其他的保存在lazytag里面,这样可以近似O(NlogN)的运行起来。在尝试着写了只有一个lazetag的程序之后我们发现一个lazytag是不能够解决问题的,那就上两个,分别表示乘法意义上的lazytag和加法意义上的lazytag。紧接着想到pushdown操作之后我们又发现必须在向下传递lazytag的时候人为地为这两个lazytag规定一个先后顺序,排列组合一下只有两种情况:

①加法优先,即规定好segtree[root*2].value=((segtree[root*2].value+segtree[root].add)*segtree[root].mul)%p,问题是这样的话非常不容易进行更新操作,假如改变一下add的数值,mul也要联动变成奇奇怪怪的分数小数损失精度,我们内心是很拒绝的;

②乘法优先,即规定好segtree[root*2].value=(segtree[root*2].value*segtree[root].mul+segtree[root].add*(本区间长度))%p,这样的话假如改变add的数值就只改变add,改变mul的时候把add也对应的乘一下就可以了,没有精度损失,看起来很不错。

讲解的时候举实例就好

比如3-3位置是5

操作 1-5  *2

然后 1-3  +4

然后 1-3  *3

此时位置3-3的值应该为(5*2+4)*3=42

1-5   乘标签2  加标签0

1-3   乘标签2  加标签0

1-5   乘标签0  加标签0

1-3   乘标签2  加标签4

1-3   乘标签6  加标签12

1-3   乘标签6  加标签12

5*6+12

就是父亲在获得标签之后会立马往下传一次,然后自己就没有了,所以能保证标签的顺序

接着贴上详细注释版代码:

  1 #include <iostream>
  2 #include <cstdio>
  3 using namespace std;
  4 //题目中给的p
  5 int p;
  6 //暂存数列的数组
  7 long long a[100007];
  8 //线段树结构体,v表示此时的答案,mul表示乘法意义上的lazytag,add是加法意义上的
  9 struct node{
 10     long long v, mul, add;
 11 }st[400007];
 12 //buildtree
 13 void bt(int root, int l, int r){
 14 //初始化lazytag
 15     st[root].mul=1;
 16     st[root].add=0;
 17     if(l==r){
 18         st[root].v=a[l];
 19     }
 20     else{
 21         int m=(l+r)/2;
 22         bt(root*2, l, m);
 23         bt(root*2+1, m+1, r);
 24         st[root].v=st[root*2].v+st[root*2+1].v;
 25     }
 26     st[root].v%=p;
 27     return ;
 28 }
 29 //核心代码,维护lazytag
 30 void pushdown(int root, int l, int r){
 31     int m=(l+r)/2;
 32 //根据我们规定的优先度,儿子的值=此刻儿子的值*爸爸的乘法lazytag+儿子的区间长度*爸爸的加法lazytag
 33     st[root*2].v=(st[root*2].v*st[root].mul+st[root].add*(m-l+1))%p;
 34     st[root*2+1].v=(st[root*2+1].v*st[root].mul+st[root].add*(r-m))%p;
 35 //很好维护的lazytag
 36     st[root*2].mul=(st[root*2].mul*st[root].mul)%p;
 37     st[root*2+1].mul=(st[root*2+1].mul*st[root].mul)%p;
 38     st[root*2].add=(st[root*2].add*st[root].mul+st[root].add)%p;
 39     st[root*2+1].add=(st[root*2+1].add*st[root].mul+st[root].add)%p;
 40 //把父节点的值初始化
 41     st[root].mul=1;
 42     st[root].add=0;
 43     return ;
 44 }
 45 //update1,乘法,stdl此刻区间的左边,stdr此刻区间的右边,l给出的左边,r给出的右边
 46 void ud1(int root, int stdl, int stdr, int l, int r, long long k){
 47 //假如本区间和给出的区间没有交集
 48     if(r<stdl || stdr<l){
 49         return ;
 50     }
 51 //假如给出的区间包含本区间
 52     if(l<=stdl && stdr<=r){
 53         st[root].v=(st[root].v*k)%p;
 54         st[root].mul=(st[root].mul*k)%p;
 55         st[root].add=(st[root].add*k)%p;
 56         return ;
 57     }
 58 //假如给出的区间和本区间有交集,但是也有不交叉的部分
 59 //先传递lazytag
 60     pushdown(root, stdl, stdr);
 61     int m=(stdl+stdr)/2;
 62     ud1(root*2, stdl, m, l, r, k);
 63     ud1(root*2+1, m+1, stdr, l, r, k);
 64     st[root].v=(st[root*2].v+st[root*2+1].v)%p;
 65     return ;
 66 }
 67 //update2,加法,和乘法同理
 68 void ud2(int root, int stdl, int stdr, int l, int r, long long k){
 69     if(r<stdl || stdr<l){
 70         return ;
 71     }
 72     if(l<=stdl && stdr<=r){
 73         st[root].add=(st[root].add+k)%p;
 74         st[root].v=(st[root].v+k*(stdr-stdl+1))%p;
 75         return ;
 76     }
 77     pushdown(root, stdl, stdr);
 78     int m=(stdl+stdr)/2;
 79     ud2(root*2, stdl, m, l, r, k);
 80     ud2(root*2+1, m+1, stdr, l, r, k);
 81     st[root].v=(st[root*2].v+st[root*2+1].v)%p;
 82     return ;
 83 }
 84 //访问,和update一样
 85 long long query(int root, int stdl, int stdr, int l, int r){
 86     if(r<stdl || stdr<l){
 87         return 0;
 88     }
 89     if(l<=stdl && stdr<=r){
 90         return st[root].v;
 91     }
 92     pushdown(root, stdl, stdr);
 93     int m=(stdl+stdr)/2;
 94     return (query(root*2, stdl, m, l, r)+query(root*2+1, m+1, stdr, l, r))%p;
 95 }
 96 int main(){
 97     int n, m;
 98     scanf("%d%d%d", &n, &m, &p);
 99     for(int i=1; i<=n; i++){
100         scanf("%lld", &a[i]);
101     }
102     bt(1, 1, n);
103     while(m--){
104         int chk;
105         scanf("%d", &chk);
106         int x, y;
107         long long k;
108         if(chk==1){
109             scanf("%d%d%lld", &x, &y, &k);
110             ud1(1, 1, n, x, y, k);
111         }
112         else if(chk==2){
113             scanf("%d%d%lld", &x, &y, &k);
114             ud2(1, 1, n, x, y, k);
115         }
116         else{
117             scanf("%d%d", &x, &y);
118             printf("%lld\n", query(1, 1, n, x, y));
119         }
120     }
121     return 0;
122 }

转载于:https://www.cnblogs.com/Renyi-Fan/p/8135010.html

P3373 【模板】线段树 2相关推荐

  1. 洛谷(P3373)线段树加乘混合模板

    题目链接:P3373 [模板]线段树 2 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 这道题目的意思很明确,就是要我们在线完成区间的乘和加运算并支持查询区间和的一个问题.处理这道 ...

  2. hdu3966 树链剖分点权模板+线段树区间更新/树状数组区间更新单点查询

    点权树的模板题,另外发现树状数组也是可以区间更新的.. 注意在对链进行操作时方向不要搞错 线段树版本 #include<bits/stdc++.h> using namespace std ...

  3. 洛谷 p3372 模板-线段树 1

    题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.求出某区间每一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个 ...

  4. 算法模板——线段树6(二维线段树:区域加法+区域求和)(求助phile)

    实现功能--对于一个N×M的方格,1:输入一个区域,将此区域全部值作加法:2:输入一个区域,求此区域全部值的和 其实和一维线段树同理,只是不知道为什么速度比想象的慢那么多,求解释...@acphile ...

  5. ACM模板——线段树树状数组ST表

    int bit[maxn],n; void init() {n = maxn;memset(bit,0,sizeof(bit)); } int sum(int i) {int s = 0;while( ...

  6. 【luogu3373】模板 线段树 2

    题面 已知一个数列,你需要进行下面三种操作: 1.将某区间每一个数乘上x 2.将某区间每一个数加上x 3.求出某区间每一个数的和 题解 区间修改+区间查询. 维护两个LazyTag #include& ...

  7. 模板三连击:树状数组+线段树+主席树

    没事儿干,复习模板...... 1.树状数组 本来不想写这个的,但是反正就几分钟就打完了,所以就写了,水AC数. 洛谷 P3374 [模板]树状数组 1 1 #include<cstdio> ...

  8. 洛谷3373 线段树模板

    题目详情:https://www.luogu.org/problemnew/show/P3373 这个线段树模板写的头疼(最后纠错发现一个long long没开差点一口血喷出来),思路就是在普通的求区 ...

  9. 线段树合集——杨子曰算法

    线段树合集--杨子曰算法 这里我把我写的五篇线段树汇总一下: 线段树(一):主要讲了线段树是什么鬼,以及怎样query(←想知道它是什么meaning,点进去!) 线段树(二):体现了线段树真正的价值 ...

最新文章

  1. android studio 无线手机调试插件,Android Studio ADB Wifi 无线调试
  2. Linux+Jenkins自动构建服务器包
  3. php简单学生管理系统设计与实现,基于PHP的学生成绩管理系统的设计与实现.doc...
  4. java基础巩固笔记(6)-注解
  5. 【LeetCode笔记】322. 零钱兑换(Java、动态规划)
  6. list接口中的常用方法例子
  7. Tensorflow -mofan1
  8. iOS边练边学--UITableViewCell的常见属性设置
  9. mysql 把主键当外键_MySQL主键和外键使用及说明
  10. windows进程管理问题
  11. jsp模糊查询_[内附完整源码和文档] 基于JSP+Servlet校园二手交易平台
  12. php 获取TZ时间格式
  13. kindeditor在Firefoxt 和 Chrome 下不能取到值的解决方法
  14. C++中volatile的作用
  15. python tcp socket.connect() [Errno 56] Socket is already connectedconnect
  16. 我认识的文华财经指标期货高手一年时间从2万到30万的实际经历
  17. 基于51单片机PWM(即脉冲宽度调制)调速数码管显示测速L298M芯片控制直流电机正反运转的项目
  18. mysql useing查询_MySQL查询优化一例——也说说 Using intersect
  19. 无人驾驶大巴试车_中国无人驾驶汽车高速公路试车成功
  20. 租房微信小程序--基于微信云开发--小程序端集成了管理员后台

热门文章

  1. TIOBE 8 月编程语言:C、Java 差距拉大,R 语言盛行
  2. [svc]arp协议的细枝末节
  3. core data 学习笔记
  4. [hihocoder 1075] 开锁魔法III
  5. 使用wireshark工具获得直连设备的IP地址
  6. Java面向对象编程思想
  7. 深入理解PHP原理之Opcodes
  8. Werkzeug源码阅读笔记(四)
  9. Cocos2d-x3.2 重力感应
  10. windows server 2008 R2 初试Hyper-V(一)