P3373 【模板】线段树 2(乘法与加法)(内附封面)

news/2024/10/17 12:28:29/

【模板】线段树 2

题目描述

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

  • 将某区间每一个数乘上 x x x
  • 将某区间每一个数加上 x x x
  • 求出某区间每一个数的和。

输入格式

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

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

接下来 q q q 行每行包含若干个整数,表示一个操作,具体如下:

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

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

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

输出格式

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

样例 #1

样例输入 #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

提示

【数据范围】

对于 30 % 30\% 30% 的数据: n ≤ 8 n \le 8 n8 q ≤ 10 q \le 10 q10
对于 70 % 70\% 70% 的数据:$n \le 10^3 , , q \le 10^4$。
对于 100 % 100\% 100% 的数据: 1 ≤ n ≤ 1 0 5 1 \le n \le 10^5 1n105 1 ≤ q ≤ 1 0 5 1 \le q \le 10^5 1q105

除样例外, m = 571373 m = 571373 m=571373

(数据已经过加强 _

样例说明:

故输出应为 17 17 17 2 2 2 40 m o d 38 = 2 40 \bmod 38 = 2 40mod38=2)。

大致思路

线段树模板,不过多解释,

  • 建树

首先,线段树是二叉树,因此具有二叉树的性质,其左儿子节点与右儿子节点是固定的,具体实现如下,其中, l c ( x ) lc(x) lc(x)为左儿子, r c ( x ) rc(x) rc(x)为右儿子(对应2n与2+1)

#define lc(x) (x<<1)
#define rc(x) ((x<<1)|1)

其次,线段树的建立为递归建立,最底层的节点对应的就是 a [ 1... n ] a[1...n] a[1...n]

void build(int x,int l,int r){tag_add[x]=0;tag_mul[x]=1;if(l==r){sm[x]=a[l];return;}int mid=(l+r)>>1;build(lc(x),l,mid);build(rc(x),mid+1,r);pushup(x);return;
}

s m [ x ] = s m [ l c ( x ) ] + s m [ r c ( x ) ] sm[x]=sm[lc(x)]+sm[rc(x)] sm[x]=sm[lc(x)]+sm[rc(x)]通常会被单独写做一个函数pushup

  • pushup

void pushup(int x){sm[x]=(sm[lc(x)]+sm[rc(x)])%mod;
}

区间修改与查询

单点修改与查询只需如同建树一样查找到节点修改并pushup或return即可,不过多赘述。

对于区间修改,我们需要用到 lazy_tag 对于一次修改操作我们先不全部进行修改,当火烧眉毛不得不用到这个值时再进行修改,对于一种运算使用一个tag[]数组实现。
此模板题有两种运算,因此用 tag_add 与 tag_mul 分别记录

int sm[N<<2],a[N<<2],tag_add[N<<2],tag_mul[N<<2];
  • cover

  • 两种运算,我们先乘后加
  • 对于乘法,节点 x 对应的 sm[x] 就是一段区间的和,根据乘法分配律,我们直接 s m [ x ] ∗ m u l sm[x]*mul sm[x]mul 即可,同样, tag_add也要乘mul,已有的 tag_mul 根据乘法结合律,直接 t a g . m u l [ x ] ∗ m u l tag.mul [ x ] * mul tag.mul[x]mul,记得最后取模
void cover(int x,int l,int r,int ad,int mul){sm[x]=sm[x]*mul%mod;sm[x]+=(r-l+1)*ad%mod;sm[x]%=mod;tag_mul[x]*=mul;tag_mul[x]%=mod;tag_add[x]*=mul;tag_add[x]+=ad;tag_add[x]%=mod;
}
  • pushdown

  • 实现 tag 下传,配合cover使用,分别下传到左儿子和右儿子,之后清空父节点的 lazy_tag 。
void pushdown(int x,int l,int r){int mid=(l+r)>>1;cover(lc(x),l,mid,tag_add[x],tag_mul[x]);cover(rc(x),mid+1,r,tag_add[x],tag_mul[x]);tag_add[x]=0;tag_mul[x]=1;
}
  • update

  • 关键部分
  • 实现区间加法与区间乘法,同样配合 cover,pushdown 使用。
  • 以下给出两种写法,将注释掉的内容解开并将 if (L<=mid)…两行注释即为第二种写法
void update(int x,int l,int r,int L,int R,int ad,int mul){
//	if(r<L||l>R)return;if(l>=L&&R>=r){cover(x,l,r,ad,mul);//若已被完全包含,进行一次计算return;}pushdown(x,l,r);//注意下传tagint mid=(l+r)>>1;if(L<=mid)update(lc(x),l,mid,L,R,ad,mul);//下传左儿子if(R>mid) update(rc(x),mid+1,r,L,R,ad,mul);//下传右儿子
//	update(lc(x),l,mid,L,R,ad,mul);
//	update(rc(x),mid+1,r,L,R,ad,mul);pushup(x);
}
int query(int x,int l,int r,int L,int R){
//	if(r<L||l>R)return 0;int res=0;if(l>=L&&R>=r){return sm[x];}pushdown(x,l,r);int mid=(l+r)>>1;if(L<=mid)res+=(query(lc(x),l,mid,L,R)%mod);if(R>mid) res+=(query(rc(x),mid+1,r,L,R)%mod);
//	res+=(query(lc(x),l,mid,L,R)%mod);
//	res+=(query(rc(x),mid+1,r,L,R)%mod);return res%mod;
}
int query(int x,int l,int r,int L,int R){if(r<L||l>R)return 0;if(l>=L&&R>=r){return sm[x];}pushdown(x,l,r);int mid=(l+r)>>1;return (query(lc(x),l,mid,L,R)+query(rc(x),mid+1,r,L,R))%mod;
}
真的快被线段树ex吐了

AC CODE

#include<bits/stdc++.h>
using namespace std;
#define int long long int
const int N=1e6+2233;
#define lc(x) (x<<1)
#define rc(x) ((x<<1)|1)
int n,m,mod;
int sm[N<<2],a[N<<2],tag_add[N<<2],tag_mul[N<<2];
void pushup(int x){sm[x]=(sm[lc(x)]+sm[rc(x)])%mod;
}
void build(int x,int l,int r){tag_add[x]=0;tag_mul[x]=1;if(l==r){sm[x]=a[l];return;}int mid=(l+r)>>1;build(lc(x),l,mid);build(rc(x),mid+1,r);pushup(x);return;
}
void cover(int x,int l,int r,int ad,int mul){sm[x]=sm[x]*mul%mod;sm[x]+=(r-l+1)*ad%mod;sm[x]%=mod;tag_mul[x]*=mul;tag_mul[x]%=mod;tag_add[x]*=mul;tag_add[x]+=ad;tag_add[x]%=mod;
}
void pushdown(int x,int l,int r){int mid=(l+r)>>1;cover(lc(x),l,mid,tag_add[x],tag_mul[x]);cover(rc(x),mid+1,r,tag_add[x],tag_mul[x]);tag_add[x]=0;tag_mul[x]=1;
}
void update(int x,int l,int r,int L,int R,int ad,int mul){
//	if(r<L||l>R)return;if(l>=L&&R>=r){cover(x,l,r,ad,mul);return;}pushdown(x,l,r);int mid=(l+r)>>1;if(L<=mid)update(lc(x),l,mid,L,R,ad,mul);if(R>mid) update(rc(x),mid+1,r,L,R,ad,mul);
//	update(lc(x),l,mid,L,R,ad,mul);
//	update(rc(x),mid+1,r,L,R,ad,mul);pushup(x);
}
int query(int x,int l,int r,int L,int R){
//	if(r<L||l>R)return 0;int res=0;if(l>=L&&R>=r){return sm[x];}pushdown(x,l,r);int mid=(l+r)>>1;if(L<=mid)res+=(query(lc(x),l,mid,L,R)%mod);if(R>mid) res+=(query(rc(x),mid+1,r,L,R)%mod);
//	res+=(query(lc(x),l,mid,L,R)%mod);
//	res+=(query(rc(x),mid+1,r,L,R)%mod);return res%mod;
}
signed main(){scanf("%lld %lld %lld",&n,&m,&mod);for(int i=1;i<=n;i++){scanf("%lld",&a[i]);}build(1,1,n);while(m--){int op,xx,yy,kk;scanf("%lld",&op);if(op==1){scanf("%lld %lld %lld",&xx,&yy,&kk);update(1,1,n,xx,yy,0,kk);}if(op==2){scanf("%lld %lld %lld",&xx,&yy,&kk);update(1,1,n,xx,yy,kk,1);}if(op==3){scanf("%lld %lld",&xx,&yy);printf("%lld\n",query(1,1,n,xx,yy));}}return 0;
}

附封面(佐仓大法好!)

请添加图片描述


http://www.ppmy.cn/news/998265.html

相关文章

aws中opensearch 日志通(Centralized Logging with OpenSearch)2.0(一)

aws日志通2.0 实现全面的日志管理和分析功能 一体化日志摄取 &#xff1a;把aws服务器日志和应用日志传输到opensearch域中无代码日志处理 &#xff1a;在网页控制台中就可以实现数据处理开箱即用 &#xff1a;提供可视化模版&#xff08;nginx、HTTP server &#xff09; 架构…

自定义MVC增删改查

目录 mymvcdemo是自定义mvc框架的使用示例 1.1 实体类 1.2 dao方法 1.3 写Service / biz 三层架构 1.4 建action 相当于selvert 1.5 con连接MySQL 8.0 版本 1.6 配置文件 XML 1.7 主界面布局 1.8 增加界面布局 1.9 写tld配置文件 2.0 注意架包 我是已经打包好的 mymv…

Expectation (Easy Version) 2023“钉耙编程”中国大学生算法设计超级联赛(5)hdu7330

Problem - 7330 题目大意&#xff1a;有n次游戏&#xff0c;每次游戏有a/b的概率获胜&#xff0c;且相互独立&#xff0c;如果当前赢了cnt次游戏&#xff0c;那么这次游戏会赢得的分数&#xff0c;问最后得分的期望 1<n<1e6;1<m,a<b<998244353 思路&#xff…

PHP高级检索功能的实现以及动态拼接sql

我们学习了解了这么多关于PHP的知识&#xff0c;不知道你们对PHP高级检索功能的实现以及动态拼接sql是否已经完全掌握了呢&#xff0c;如果没有&#xff0c;那就跟随本篇文章一起继续学习吧! PHP高级检索功能的实现以及动态拼接sql。完成的功能有&#xff1a;可以单独根据一个…

开源项目-知识库管理系统(中国软件杯项目)

简述 哈喽,大家好,今天带来一个开源项目-知识库管理系统,项目通过Spring MVC技术实现。通过readme了解到这是某位大神大三暑假(2016年)参加第五届中国软件杯项目的源码。由三人团队完成(Yu yufeng\Zhou changqin\Liu chenzhe) 此作品获得了本科组全国二等奖。项目本身用…

LeetCode 39. 组合总和(回溯+剪枝)

题目&#xff1a; 链接&#xff1a;LeetCode 39. 组合总和 难度&#xff1a;中等 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#xff0c;并以列表形式返回。你可以按 …

零基础学习编程(前端、Java、Python、大数据……)的一些建议

一、学习要明确动机和方向&#xff0c;有强烈的学习欲望 就自学前端来说&#xff0c;很多时候你其实都是孤独的&#xff0c;不知道到底学得怎么样&#xff0c;除非有强烈的欲望&#xff0c;不然大部分的新手很容易就会半途而废。 首先&#xff0c;要想明白自己学习编程的强烈…

MySQL 在CentOS下安装

yum安装 1、yum源安装 yum install mariadb-server2、启动MySQL服务 systemctl start mariadb3、查看运行状态 systemctl status mariadb4、设置初始密码 mysql -u rootuse mysql;update user set passwordpassword("root")where userroot;flush privileges;e…