线段树_区间加乘(洛谷P3373模板)
题目描述
如题,已知一个数列,你需要进行下面三种操作:
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的结果。
线段树维护区间加、区间乘。
相比最普通的线段树,我们这里在每一个节点都存两个标记:
mark[ x ][ 0 ](加标记)表示对于节点x所表示的一整段区间,每个数通过加法操作所增加的值(就是说先加a再乘b再加c之后,这里存的数是a*b+c)
mark[ x ][ 1 ](乘标记)表示对于节点x所表示的一整段区间,每个数被乘了几次。
p[ x ]表示的一整段区间中每一个数的和是多少。
接下来考虑各种操作,假设我们要对[L,R]的每一个数进行操作,当前正在处理编号为x的[l,r]的区间(这里保证了不存在r<L或R<l,即[l,r]与[L,R]无交集,这种情况不做任何处理直接return就好)。
如果进行加操作,当L<=l,r<=R,即当前区间被完整覆盖,我们将mark[ x ][ 0 ]直接加上加的数,再更新一下p[ x ]的值。若当前区间不被完整覆盖,就pushdown一下,再处理递归子节点就好。
如果进行乘操作,当L<=l,r<=R,即当前区间被完整覆盖,我们将mark[ x ][ 1 ]与mark[ x ][ 0 ]都直接乘上乘的数,更新一下p[ x ]的值(因为从这之前这个区间每一个数通过加法操作所增加的数都被乘了)。若当前区间不被完整覆盖,同样的,就pushdown一下,再处理递归子节点就好。
接下来就是较为复杂的pushdown了:
首先pushdown的一大意义在于,一个区间上的标记表示的是区间内每一个数都具备的特点,当我们需要pushdown时,就说明我们对区间内部子区间进行操作,导致区间的标记不再具有普遍性,我们需要把标记向下传递,并把当前区间的标记清空。
所以pushdown分为以下几个部分:
1、将左右儿子区间的乘标记都乘上本区间的乘标记。
2、将左右儿子区间的加标记都先乘上本区间的乘标记,再加上本区间的加标记。
3、将左右儿子的区间和的值先乘上本区间乘标记,再分加上本区间加标记与左右儿子区间长度的乘积。
4、将本区间的标记清空,再在对左右儿子操作完成后用左右儿子的区间和更新本区间的区间和。
然后就是代码了。
- #include<algorithm>
- #include<iostream>
- #include<cstring>
- #include<cstdio>
- #include<cmath>
- #define LL long long
- #define M 500000
- #define mid (l+r>>1)
- using namespace std;
- LL read(){
- LL nm=0ll,fh=1ll;char cw=getchar();
- for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
- for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
- return fh*nm;
- }
- LL n,mod,q,p[M],mark[M][2],a[M],L,R,qs,num;
- void build(LL x,LL l,LL r){
- mark[x][1]=1;
- if(l==r){p[x]=a[l];return;}
- build(x<<1,l,mid),build(x<<1|1,mid+1,r);
- p[x]=(p[x<<1]+p[x<<1|1])%mod;
- }
- void up(LL x){p[x]=(p[x<<1]+p[x<<1|1])%mod;}
- void pushdown(LL x,LL l,LL r){
- p[x<<1]=p[x<<1]*mark[x][1]%mod;
- p[x<<1]=(p[x<<1]+(mark[x][0]*(mid-l+1)))%mod;
- p[x<<1|1]=p[x<<1|1]*mark[x][1]%mod;
- p[x<<1|1]=(p[x<<1|1]+(mark[x][0]*(r-mid)))%mod;
- mark[x<<1][1]=mark[x<<1][1]*mark[x][1]%mod;
- mark[x<<1][0]=(mark[x<<1][0]*mark[x][1]+mark[x][0])%mod;
- mark[x<<1|1][1]=mark[x<<1|1][1]*mark[x][1]%mod;
- mark[x<<1|1][0]=(mark[x<<1|1][0]*mark[x][1]+mark[x][0])%mod;
- mark[x][0]=0ll,mark[x][1]=1ll;
- }
- void add(LL x,LL l,LL r,LL m){
- if(r<L||R<l) return;
- if(L<=l&&r<=R){
- mark[x][0]=(mark[x][0]+m)%mod;
- p[x]=(p[x]+(m*(r-l+1)))%mod;
- return;
- }
- pushdown(x,l,r);
- add(x<<1,l,mid,m),add(x<<1|1,mid+1,r,m);
- up(x);
- }
- void mult(LL x,LL l,LL r,LL m){
- if(r<L||R<l) return;
- if(L<=l&&r<=R){
- mark[x][0]=mark[x][0]*m%mod;
- mark[x][1]=mark[x][1]*m%mod;
- p[x]=p[x]*m%mod;
- return;
- }
- pushdown(x,l,r);
- mult(x<<1,l,mid,m),mult(x<<1|1,mid+1,r,m);
- up(x);
- }
- LL query(LL x,LL l,LL r){
- if(r<L||R<l) return 0ll;
- LL ans;
- if(L<=l&&r<=R) ans=p[x]%mod;
- else{
- pushdown(x,l,r);
- ans=(query(x<<1,l,mid)+query(x<<1|1,mid+1,r))%mod;
- up(x);
- }
- return ans;
- }
- int main(){
- n=read(),q=read(),mod=read();
- for(LL i=1;i<=n;i++) a[i]=read();
- build(1,1,n);
- while(q--){
- qs=read(),L=read(),R=read();
- if(qs==3) printf("%lld\n",query(1,1,n)%mod);
- else{
- num=read();
- if(qs==2) add(1,1,n,num);
- else mult(1,1,n,num);
- }
- }
- return 0;
- }
线段树_区间加乘(洛谷P3373模板)的更多相关文章
- 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)
To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...
- [bzoj2962]序列操作_线段树_区间卷积
序列操作 bzoj-2962 题目大意:给定一个n个数的正整数序列,m次操作.支持:1.区间加:2.区间取相反数:3.区间求选c个数的乘积和. 注释:$1\le n,m\le 5\cdot 10^4$ ...
- 线段树入门详解,洛谷P3372 【模板】线段树 1
关于线段树: 本随笔参考例题 P3372 [模板]线段树 1 所谓线段树就是把一串数组拆分成一个一个线段形成的一棵树. 比如说像这样的一个数组1,2,3,4,5: 1 ~ 5 / ...
- HDU_3308_线段树_区间合并
LCIS Time Limit: 6000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submis ...
- 洛谷 P3373 【模板】线段树 2
洛谷 P3373 [模板]线段树 2 洛谷传送门 题目描述 如题,已知一个数列,你需要进行下面三种操作: 将某区间每一个数乘上 xx 将某区间每一个数加上 xx 求出某区间每一个数的和 输入格式 第一 ...
- 洛谷P3373 【模板】线段树 2 && P2023 [AHOI2009]维护序列——题解
题目传送: P3373 [模板]线段树 2 P2023 [AHOI2009]维护序列 该题较传统线段树模板相比多了一个区间乘的操作.一提到线段树的区间维护问题,就自然想到了“懒标记”:为了降低时间复 ...
- 洛谷——P3373 【模板】线段树 2&& B 数据结构
P3373 [模板]线段树 2 题目描述 如题,已知一个数列,你需要进行下面三种操作: 1.将某区间每一个数乘上x 2.将某区间每一个数加上x 3.求出某区间每一个数的和 线段树维护区间乘法 1.如何 ...
- UVA 12436-Rip Van Winkle's Code(线段树的区间更新)
题意: long long data[250001]; void A( int st, int nd ) { for( int i = st; i \le nd; i++ ) data[i] = da ...
- 刷题向》关于线段树的区间开根号 BZOJ3211(NORMAL+)
这是一道关于线段树的区间开根号的裸题,没什么好讲的. 值得注意的是,因为有区间开根号的性质,所以我们每一次更改操作只能把更改区间所覆盖的所有元素全部查找,当然你直接找效率明显爆炸... 能够注意到,指 ...
随机推荐
- Linux下apache安装php
php 1.下载解压 cd /usr/local/src wget http://mirrors.sohu.com/php/php-5.6.9.tar.gz tar zxvf php-5.6.9.ta ...
- Keepalived 集群在Linux下的搭建
[概述]:Keepalived 是一个免费开源的,用C编写.主要提供loadbalancing(负载均衡)和 high-availability(高可用)功能,负载均衡实现需要依赖Linux的虚拟服务 ...
- hdu1695(容斥 or 莫比乌斯反演)
刚开始看题,想了一会想到了一种容斥的做法.复杂度O( n(3/2) )但是因为题目上说有3000组测试数据,然后吓尿.完全不敢写. 然后想别的方法. 唉,最近精神有点问题,昨天从打完bc开始想到1点多 ...
- Webpack探索【2】--- 安装、项目初始化、webpack.config.js配置文件
本文主要讲安装.项目初始化.webpack.config.js配置文件方面的内容.
- Linux安装Nginx使用反向代理
nginx的反向代理功能(自带了反向代理的功能,天生的二道贩子)1.实验环境准备准备2个服务器,都安装好nginx软件nginx1 192.168.13.79 作为web服务器 (理解为火车票售票点) ...
- linux c编程:进程控制(一)
一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程, 也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同 ...
- 阿里云centos7搭建php+nginx环境
阿里云Centos搭建lnmp(php7.1+nginx+mysql5.7) https://jingyan.baidu.com/article/215817f7a10bfb1eda14238b.ht ...
- python __name__及__main()__的妙处
#hello.py def sayHello(): str="hello" print(str); if __name__ == "__main__": pri ...
- 华为机试ACM(字符组合问题)
今晚做了华为的机试,3道ACM题,最后一道是实现从M个不同字符中任取N个字符的所有组合. eg: input:ABC 2 output:AB AC BC 第一个输入为字符串,第二个输入为组合的字符个数 ...
- awk 字符串函数
awk 提供了许多强大的字符串函数,见下表: awk 内置字符串函数 gsub(r,s) 在整个 $0 中用 s 替代 r gsub(r,s,t) 在整个 t 中用 s 替代 r index(s,t) ...