题目描述

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

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的结果。

 

1.构建树
更新$a_i$的值时,需要对所有包含$i$这个位置的结点的值重新计算。
对于线段树中的每一个非叶子节点$[a,b]$,它的左儿子表示的区间为$[a,(a+b)/2]$,右儿子表示的区间为$[(a+b)/2+1,b]$。我们就可以得出节点i的权值=她的左儿子权值+她的右儿子权值。一颗二叉树,她的左儿子和右儿子编号分别是她*2和她*2+1
 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的更多相关文章

  1. HDU 1166 敌兵布阵(线段树/树状数组模板题)

    敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submi ...

  2. P3380 【模板】二逼平衡树(树套树)(线段树套平衡树)

    P3380 [模板]二逼平衡树(树套树) 前置芝士 P3369 [模板]普通平衡树 线段树套平衡树 这里写的是线段树+splay(不吸氧竟然卡过了) 对线段树的每个节点都维护一颗平衡树 每次把给定区间 ...

  3. hdu1698(线段树区间替换模板)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1698 题意: 第一行输入 t 表 t 组测试数据, 对于每组测试数据, 第一行输入一个 n , 表示 ...

  4. 【Luogu】P3380树套树模板(线段树套Splay)

    题目链接 幸甚至哉,歌以咏志. 拿下了曾经是那么遥不可及的线段树,学会了曾经高不可攀的平衡树,弄懂了装B的时候才挂在嘴边的树套树. 每道模板都是链上的一颗珠子.把它们挨个串起来,就成为我成长的历程. ...

  5. HDU 1166 线段树模板&树状数组模板

    HDU1166 上好的线段树模板&&树状数组模板 自己写的第一棵线段树&第一棵树状数组 莫名的兴奋 线段树: #include <cstdio> using nam ...

  6. 洛谷 P3380 【模板】二逼平衡树(树套树)-线段树套splay

    P3380 [模板]二逼平衡树(树套树) 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 查询k在区间内的排名 查询区间内排名为k的值 修改某一位值上的数 ...

  7. 敌兵布阵---hud1166(线段树或者树状数组模板)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1166 线段树中对某一点的值进行改变: #include<iostream> #includ ...

  8. 洛谷P3380 【模板】二逼平衡树(树套树)(线段树+树状数组)

    P3380 [模板]二逼平衡树(树套树) 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 查询k在区间内的排名 查询区间内排名为k的值 修改某一位值上的数 ...

  9. 线段树(单标记+离散化+扫描线+双标记)+zkw线段树+权值线段树+主席树及一些例题

    “队列进出图上的方向 线段树区间修改求出总量 可持久留下的迹象 我们 俯身欣赏” ----<膜你抄>     线段树很早就会写了,但一直没有总结,所以偶尔重写又会懵逼,所以还是要总结一下. ...

  10. 学习笔记--函数式线段树(主席树)(动态维护第K极值(树状数组套主席树))

    函数式线段树..资瓷 区间第K极值查询 似乎不过似乎划分树的效率更优于它,但是如果主席树套树状数组后,可以处理动态的第K极值.即资瓷插入删除,划分树则不同- 那么原理也比较易懂: 建造一棵线段树(权值 ...

随机推荐

  1. 061、Java中利用return结束方法调用

    01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public ...

  2. ORACLE添加新用户并配置权限 添加其他用户的表权限

    添加用户配置权限 1.查出表空间所在位置 ,file_name from dba_data_files order by file_id; 2.根据步骤1查出的路径.将路径替换掉并指定用户名 路径:D ...

  3. maven详解之 pom.xml 解释

    <project>:pom.xml的根标签,一个maven项目用一对<peoject></project>标签包裹. <modelVersion>4.0 ...

  4. JAVA面试题0 JAVA语言的基本知识

    JAVA语言的特点是什么? >面向对象:两个基本概念:类和对象:三大基本特性:封装.继承.多态: >健壮性:java吸收了C/C++的优点,但是去掉了它们影响健壮性的部分,例如指针和内存申 ...

  5. Java 文件

    章节 Java 基础 Java 简介 Java 环境搭建 Java 基本语法 Java 注释 Java 变量 Java 数据类型 Java 字符串 Java 类型转换 Java 运算符 Java 字符 ...

  6. Android明密文切换

    前言: 在我们的登录界面经常会遇到查看自己输入密码是否正确,就会用到明密文切换 正文: 我们先写出xml文件文件中的代码,不用过多解释 <EditText android:layout_widt ...

  7. node - 读取cookie

    req.headers.cookie

  8. SQL语句--分组统计

    一.教师号 星期号 是否有课1 2 有1 3 有2 1 有3 2 有`1 2 有写一条sql语句让你变为这样的表教师号 星期一 星期二 星期三1 2 12 13 1各星期下的数字表示:对应的教师在星期 ...

  9. 如何更改RStudio(或R)中的默认目录

    方法一: Session -> Set Working Directory -> Choose Directory ... or shortcut (Ctrl+Shift+H) 方法二 s ...

  10. 《学习R》笔记:科学计算器、检查变量和工作区、向量、矩阵和数组、列表和数据框

    一.第二章 科学计算器 要检查两个数字是否一样,要使用 all.equal() ,不要使用 == ,== 符号仅用于比较两个整型数是否存在相同 . > all.equal(sqrt(2)^2,2 ...