线段树--线段树【模板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极值.即资瓷插入删除,划分树则不同- 那么原理也比较易懂: 建造一棵线段树(权值 ...
随机推荐
- html基础与入门
html就是指一个html文件,它是由各种标签组成的 html分为 < !DOCTYPE html > 和 Head 和 Body Head title+meta+link+style B ...
- python 定义一个空集合、空字典
s = set() #定义一个空集合 s = {} #定义一个空字典
- Machine Learning Note Phase 1( Done!)
Machine Learning 这是第一份机器学习笔记,创建于2019年7月26日,完成于2019年8月2日. 该笔记包括如下部分: 引言(Introduction) 单变量线性回归(Linear ...
- List实体类、DataTable与xml互转
序列化常用Attribute讲解说明 [XmlRootAttribute("MyCity", Namespace="abc.abc", IsNullable=f ...
- list实体数据分组
比如查询获取了60000条数据进行批量插入数据库,一次直接插入6万可能不是很好,可以将6万条数据按照5000分成几组,每组批量插入5000条 List<T> list = new List ...
- LCT(2)
LCT(2) 关于 LCT 的基本操作和代码实现见 (1) . 5. LCT的应用 5.0 LCT 裸题 就是LCT的基本操作模板题,常出现于早年省选.不讨论. 5.1 LCT维护子树信息 很多时候, ...
- 留学Essay写作常见谬误盘点
留学生在完成英语论文作业的时候总会出现各种各样的谬误,导致最后拿不到高分,甚至挂科,最终只得选择写作完成.本文小编为大家总结出我们留学生在essay写作中几个常见谬误,希望大家有则改之,无则加勉. E ...
- 关于c++静态类的说法
看了网上的观点,大致有这几种比较好的. 观点一:根据现代的C++观点,静态类没有必要存在于C++中. 一个类是对一个概念的描述,类的本质是它维护了一个不变式,也就是说它有一个状态,它所有的接口都是为了 ...
- 第五篇Django URL name 详解
Django URL name 详解 利用Django开发网站,可以设计出非常优美的url规则,如果url的匹配规则(包含正则表达式)组织得比较好,view的结构就会比较清晰,比较容易维护. Djan ...
- caffe 官方demo python api
Jupyter https://nbviewer.jupyter.org/github/BVLC/caffe/blob/master/examples/net_surgery.ipynb 涉及: - ...