线段树--线段树【模板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极值.即资瓷插入删除,划分树则不同- 那么原理也比较易懂: 建造一棵线段树(权值 ...
随机推荐
- Mongoose多表查询
文章来自 两个表关联查询aggregate 多个表关联查询aggregate populate多表关联查询 多表查询的两个方式 一个是aggregate聚合 一个是populate Schema的外表 ...
- Numpy使用大全(python矩阵相关运算大全)-Python数据分析基础2
//2019.07.10python数据分析基础——numpy(数据结构基础) import numpy as np: 1.python数据分析主要的功能实现模块包含以下六个方面:(1)numpy—— ...
- HDU - 3724 Encoded Barcodes (字典树)
题意:给定n个字符串和m个经过处理得到的字符串,问对于m个字符串中的每个字符串,n个字符串中以该字符串为前缀的个数.分析:1.误差在[0.95x, 1.05x],因此求8个数的平均数,大于平均数为1, ...
- netty权威指南学习笔记六——编解码技术之MessagePack
编解码技术主要应用在网络传输中,将对象比如BOJO进行编解码以利于网络中进行传输.平常我们也会将编解码说成是序列化/反序列化 定义:当进行远程跨进程服务调用时,需要把被传输的java对象编码为字节数组 ...
- Python 列表/元组/字典总结
序列是Python中最基本的数据结构.序列中的每个元素都分配一个数字 - 它的位置,或索引,第一个索引是0,第二个索引是1,依此类推. Python有6个序列的内置类型,但最常见的是列表和元组. 序列 ...
- STL--迭代器设计原则和萃取机制(Traits)
title: C++ STL迭代器设计原则和萃取机制(Traits) date: 2019-12-23 15:21:47 tags: STL C/C++ categories: STL 迭代器 (it ...
- 二十、JavaScript之对象
一.代码如下 二.执行效果如下 <!DOCTYPE html> <html> <meta http-equiv="Content-Type" cont ...
- 003、mysql输出多个结果
SELECT VERSION(); SELECT NOW(); 结果1: 结果2: 不忘初心,如果您认为这篇文章有价值,认同作者的付出,可以微信二维码打赏任意金额给作者(微信号:382477247)哦 ...
- 122-PHP类成员函数(三)
<?php class ren{ //定义人类 private function dance(){ //定义private成员方法dance echo '我要跳一支舞.'; } private ...
- 微信小程序添加背景图片的坑
给微信小程序页面加载背景图片解决方案 直接附上原文地址: 给微信小程序页面加载背景图片解决方案 - YUSIR 完美CODING世界 - CSDN博客 https://blog.csdn.net/y ...