线段树--线段树【模板1】P3372
题目描述
如题,已知一个数列,你需要进行下面两种操作:
1.将某区间每一个数加上$x$
2.求出某区间每一个数的和
输入格式
第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。
第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。
接下来M行每行包含3或4个整数,表示一个操作,具体如下:
操作1: 格式:1 $x$ $y$ $k$ 含义:将区间$[x,y]$内每个数加上$k$
操作2: 格式:2 $x$ $y$ 含义:输出区间$[x,y]$内每个数的和
输出格式
输出包含若干行整数,即为所有操作2的结果。
inline void add(int x, int l, int r, ll y)
{
c[x] += (r - l + ) * y;
lazy[x] += y;
}
inline void pushup(int x)
{
c[x] = c[x*] + c[x*+];
}
inline void pushdown(int x, int l, int r)
{
add(x*, l, (l+r)/, lazy[x]);
add(x*+, (l+r)/ + , r, lazy[x]);
lazy[x] = ;
}
inline void build(int x, int l, int r)
{
if(l == r)
return c[x] = a[l], void();
build(x*, l, (l+r)/);
build(x*+, (l+r)/ + , r);
pushup(x);
}
2.区间修改
1、如果这个区间被完全包括在目标区间里面,直接修改整个区间
2、如果这个区间的左儿子和目标区间有交集,那么修改左儿子
3、如果这个区间的右儿子和目标区间有交集,那么修改右儿子
inline void revise(int x, int l, int r, int l1, int r1, ll y){
if(l1 <= l && r <= r1)
return add(x, l, r, y);
if(lazy[x])
pushdown(x, l, r);
if(l1 <= (l+r)/)
revise(x*, l, (l+r)/, l1, r1, y);
if(r1 > (l+r)/)
revise(x*+, (l+r)/ + , r, l1, r1, y);
pushup(x);
}
3.区间查询
1、如果这个区间被完全包括在目标区间里面,直接返回这个区间的值
2、如果这个区间的左儿子和目标区间有交集,那么搜索左儿子
3、如果这个区间的右儿子和目标区间有交集,那么搜索右儿子
inline ll query(int x, int l, int r, int l1, int r1){
if(l1 <= l && r <= r1)
return c[x];
if(lazy[x])
pushdown(x, l, r);
ll sum = ;
if(l1 <= (l+r)/)
sum += query(x*, l, (l+r)/, l1, r1);
if(r1 > (l+r)/)
sum += query(x*+, (l+r)/ + , r, l1, r1);
return sum;
}
这道题的完整代码:
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <cstring>
#define ll long long
using namespace std;
const int N = ;
int n, m, l, r, ans;
ll k, a[N], c[N], lazy[N];
inline void add(int x, int l, int r, ll y)
{
c[x] += (r - l + ) * y;
lazy[x] += y;
}
inline void pushup(int x)
{
c[x] = c[x*] + c[x*+];
}
inline void pushdown(int x, int l, int r)
{
add(x*, l, (l+r)/, lazy[x]);
add(x*+, (l+r)/ + , r, lazy[x]);
lazy[x] = ;
}
inline void build(int x, int l, int r)
{
if(l == r)
return c[x] = a[l], void();
build(x*, l, (l+r)/);
build(x*+, (l+r)/ + , r);
pushup(x);
}
inline ll query(int x, int l, int r, int l1, int r1){
if(l1 <= l && r <= r1)
return c[x];
if(lazy[x])
pushdown(x, l, r);
ll sum = ;
if(l1 <= (l+r)/)
sum += query(x*, l, (l+r)/, l1, r1);
if(r1 > (l+r)/)
sum += query(x*+, (l+r)/ + , r, l1, r1);
return sum;
}
inline void revise(int x, int l, int r, int l1, int r1, ll y){
if(l1 <= l && r <= r1)
return add(x, l, r, y);
if(lazy[x])
pushdown(x, l, r);
if(l1 <= (l+r)/)
revise(x*, l, (l+r)/, l1, r1, y);
if(r1 > (l+r)/)
revise(x*+, (l+r)/ + , r, l1, r1, y);
pushup(x);
} int main(){
scanf("%d %d", &n, &m);
for (int i = ;i <= n;i++)
scanf("%lld", &a[i]);
build(, , n);
for (int i = ;i <= m;i++){
scanf("%d %d %d", &ans, &l, &r);
if(ans & )
scanf("%lld", &k), revise(, , n, l, r, k);
else
printf("%lld\n", query(, , n, l, r));
}
return ;
}
*接下来附上几个基本代码:
一、区间最值
1.单点替换:
const int M=;
LL a[M];
LL MAX[M<<];
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
void update(int rt){
MAX[rt]=max(MAX[rt<<],MAX[rt<<|]);
}
void build(int l,int r,int rt){
if (l==r) {
MAX[rt]=a[l];
return;
}
int m=(l+r)>>;
build(lson);
build(rson);
update(rt);
}
void modify(int l,int r,int rt,int p,int v){
if (l==r) {
MAX[rt]=v;
return;
}
int m=(l+r)>>;
if (p<=m) modify(lson,p,v);
else modify(rson,p,v);
update(rt);
} int query(int l,int r,int rt,int nowl,int nowr) {
if (nowl<=l && r<=nowr) return MAX[rt];
int ans=INT_MIN;
int m=(l+r)>>;
if (nowl<=m) ans=max(ans,query(lson,nowl,nowr));
if (m<nowr) ans=max(ans,query(rson,nowl,nowr));
return ans;
}
二、区间和
区间和是线段树可维护的最基本的信息,其他所有线段树可维护的序列信息,都是以区间和为模板建立的。
1.单点增减、单点替换:
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
void update(int rt){
sum[rt]=sum[rt<<]+sum[rt<<|];//sum[rt]表示rt节点所包含的区间信息,此处为区间和
}
void build(int l,int r,int rt){ //构造线段树
if (l==r) {
sum[rt]=a[l];
return;
}
int m=(l+r)>>;
build(lson);
build(rson);
update(rt);
}
void modify(int l,int r,int rt,int p,LL v){ //将p位置修改为v
if (l==r) {
sum[rt]=v;//如果是将p位置的数+v,则此句应为sum[rt]+=v;
return;
}
int m=(l+r)>>;
if (p<=m) modify(lson,p,v);
else modify(rson,p,v);
update(rt);
} LL query(int l,int r,int rt,int nowl,int nowr) { //询问[nowl,nowr]的信息
if (nowl<=l && r<=nowr) return sum[rt];
LL ans=;
int m=(l+r)>>;
if (nowl<=m) ans+=query(lson,nowl,nowr);
if (m<nowr) ans+=query(rson,nowl,nowr);
return ans;
}
2.区间增减、区间替换:(测试:洛谷P3372)
const int M=;
LL a[M];
LL sum[M<<],lazy[M<<];
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
void update(int rt){
sum[rt]=sum[rt<<]+sum[rt<<|];
}
void build(int l,int r,int rt){
lazy[rt]=;//懒标记
if (l==r) {
sum[rt]=a[l];
return;
}
int m=(l+r)>>;
build(lson);
build(rson);
update(rt);
}
void clean(int rt,int len){//懒标记下移
if(lazy[rt]){
lazy[rt<<]+=lazy[rt];//若为区间替换则为“=”
lazy[rt<<|]+=lazy[rt];//同上
sum[rt<<]+=lazy[rt]*(len-(len>>));//同上
sum[rt<<|]+=lazy[rt]*(len>>);//同上
lazy[rt]=;
}
}
void modify(int l,int r,int rt,int nowl,int nowr,LL v){
int len=r-l+;
if (nowl<=l&&r<=nowr) {
lazy[rt]+=v;//若为区间替换则为“=”
sum[rt]+=v*len;//同上
return;
}
clean(rt,len);//每次分治前需要下移懒标记,不分治就不下移
int m=(l+r)>>;
if(nowl<=m)modify(lson,nowl,nowr,v);
if(m<nowr)modify(rson,nowl,nowr,v);
update(rt);
}
LL query(int l,int r,int rt,int nowl,int nowr) {
if (nowl<=l && r<=nowr) return sum[rt];
clean(rt,r-l+);//同上
LL ans=;
int m=(l+r)>>;
if (nowl<=m) ans+=query(lson,nowl,nowr);
if (m<nowr) ans+=query(rson,nowl,nowr);
return ans;
}
3.区间加减乘混合(测试:洛谷P3373)
typedef long long LL;
const int M=;
LL a[M];
LL sum[M<<],add[M<<],c[M<<];
LL p;//模数
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
void update(int rt){
sum[rt]=(sum[rt<<]+sum[rt<<|])%p;
}
void build(int l,int r,int rt){
add[rt]=;c[rt]=;//加法懒标记和乘法懒标记
if (l==r) {
sum[rt]=a[l];
return;
}
int m=(l+r)>>;
build(lson);
build(rson);
update(rt);
}
void clean(int rt,int len){
if(c[rt]!=){//先下移乘法标记,再下移加法标记
c[rt<<]=c[rt<<]*c[rt]%p;
c[rt<<|]=c[rt<<|]*c[rt]%p;
add[rt<<]=add[rt<<]*c[rt]%p;//加法标记也要乘上乘数
add[rt<<|]=add[rt<<|]*c[rt]%p;
sum[rt<<]=sum[rt<<]*c[rt]%p;
sum[rt<<|]=sum[rt<<|]*c[rt]%p;
c[rt]=;
}
if(add[rt]){
add[rt<<]=(add[rt<<]+add[rt])%p;
add[rt<<|]=(add[rt<<|]+add[rt])%p;
sum[rt<<]+=add[rt]*(len-(len>>));sum[rt<<]%=p;
sum[rt<<|]+=add[rt]*(len>>);sum[rt<<|]%=p;
add[rt]=;
}
}
void modify(int l,int r,int rt,int nowl,int nowr,LL v){//区间加法
int len=r-l+;
if (nowl<=l&&r<=nowr) {
add[rt]=(add[rt]+v)%p;
sum[rt]=(sum[rt]+v*len%p)%p;
return;
}
clean(rt,len);
int m=(l+r)>>;
if(nowl<=m)modify(lson,nowl,nowr,v);
if(m<nowr)modify(rson,nowl,nowr,v);
update(rt);
}
void modify_(int l,int r,int rt,int nowl,int nowr,LL v){//区间乘法
int len=r-l+;
if(nowl<=l&&r<=nowr){
c[rt]=c[rt]*v%p;
add[rt]=add[rt]*v%p;
sum[rt]=sum[rt]*v%p;
return;
}
clean(rt,len);
int m=(l+r)>>;
if(nowl<=m)modify_(lson,nowl,nowr,v);
if(m<nowr)modify_(rson,nowl,nowr,v);
update(rt);
}
LL query(int l,int r,int rt,int nowl,int nowr) {
if (nowl<=l && r<=nowr) return sum[rt];
clean(rt,r-l+);
LL ans=;
int m=(l+r)>>;
if (nowl<=m) ans=(ans+query(lson,nowl,nowr))%p;
if (m<nowr) ans=(ans+query(rson,nowl,nowr))%p;
return ans;
}
线段树--线段树【模板1】P3372的更多相关文章
- HDU 1166 敌兵布阵(线段树/树状数组模板题)
敌兵布阵 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submi ...
- P3380 【模板】二逼平衡树(树套树)(线段树套平衡树)
P3380 [模板]二逼平衡树(树套树) 前置芝士 P3369 [模板]普通平衡树 线段树套平衡树 这里写的是线段树+splay(不吸氧竟然卡过了) 对线段树的每个节点都维护一颗平衡树 每次把给定区间 ...
- hdu1698(线段树区间替换模板)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1698 题意: 第一行输入 t 表 t 组测试数据, 对于每组测试数据, 第一行输入一个 n , 表示 ...
- 【Luogu】P3380树套树模板(线段树套Splay)
题目链接 幸甚至哉,歌以咏志. 拿下了曾经是那么遥不可及的线段树,学会了曾经高不可攀的平衡树,弄懂了装B的时候才挂在嘴边的树套树. 每道模板都是链上的一颗珠子.把它们挨个串起来,就成为我成长的历程. ...
- HDU 1166 线段树模板&树状数组模板
HDU1166 上好的线段树模板&&树状数组模板 自己写的第一棵线段树&第一棵树状数组 莫名的兴奋 线段树: #include <cstdio> using nam ...
- 洛谷 P3380 【模板】二逼平衡树(树套树)-线段树套splay
P3380 [模板]二逼平衡树(树套树) 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 查询k在区间内的排名 查询区间内排名为k的值 修改某一位值上的数 ...
- 敌兵布阵---hud1166(线段树或者树状数组模板)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1166 线段树中对某一点的值进行改变: #include<iostream> #includ ...
- 洛谷P3380 【模板】二逼平衡树(树套树)(线段树+树状数组)
P3380 [模板]二逼平衡树(树套树) 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 查询k在区间内的排名 查询区间内排名为k的值 修改某一位值上的数 ...
- 线段树(单标记+离散化+扫描线+双标记)+zkw线段树+权值线段树+主席树及一些例题
“队列进出图上的方向 线段树区间修改求出总量 可持久留下的迹象 我们 俯身欣赏” ----<膜你抄> 线段树很早就会写了,但一直没有总结,所以偶尔重写又会懵逼,所以还是要总结一下. ...
- 学习笔记--函数式线段树(主席树)(动态维护第K极值(树状数组套主席树))
函数式线段树..资瓷 区间第K极值查询 似乎不过似乎划分树的效率更优于它,但是如果主席树套树状数组后,可以处理动态的第K极值.即资瓷插入删除,划分树则不同- 那么原理也比较易懂: 建造一棵线段树(权值 ...
随机推荐
- 059、Java中定义一个有参数无返回值的方法
01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public ...
- 吴裕雄--天生自然java开发常用类库学习笔记:Map接口
import java.util.HashMap ; import java.util.Map ; public class HashMapDemo01{ public static void mai ...
- Codeforces Round #611 (Div. 3)
原题面:https://codeforces.com/contest/1283 A.Minutes Before the New Year 题目大意:给定时间,问距离零点零分还有多久? 分析:注意一下 ...
- Exchange Server备份与恢复
本文档描述了Exchange 2003.Exchange Server 2007/2010的备份与恢复操作,涉及的内容包括: 1.使用NTBackup 备份与恢复Exchange 2007/2003 ...
- CSS - flex 垂直水平居中
display: flex; justify-content: center; /* 水平居中 */ align-items: center; /* 垂直居中 */
- Redis 详解 (一) redis的简介和安装
目录 1.Redis 的简介 2.Redis 下载 3.安装环境 4.编译安装 5.启动Redis 6.关闭Redis 7.注意事项 工作中一直在用 Redis,但是一直没有进行系统的总结,这个系列的 ...
- python 第一节 脚本 import from reload exec
环境Ubuntu 14.04, 不写交互式命令行了,直接脚本开始. # first Python script import sys print(sys.platform) print(2**4) x ...
- Java singleton 单例
饿汉式,instance在类加载化时完成初始化,线程安全 package cookie; public class SingletonAtOnce { private SingletonAtOnce( ...
- flink笔记(三) flink架构及运行方式
架构图 Job Managers, Task Managers, Clients JobManager(Master) 用于协调分布式执行.它们用来调度task,协调检查点,协调失败时恢复等. Fli ...
- PHP表单处理、会话管理、文件上传、文件处理、执行函数(10.8 第十六天)
表单处理 服务器接收用户发过来的数据方式: $_GET 接收用户以GET方式发过来的数据 $_POST 接收用户以POST方式发过来的数据 $_COOKIE 接收用户COOKIE $_REQUEST ...