热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

线段树入门到自闭

线段树是一种二叉树,也就是对于一个线段,我们会用一个二叉树来表示。可以进行一些区间

算法介绍

线段树是一种二叉树,也就是对于一个线段,我们会用一个二叉树来表示。
可以进行一些区间的修改和查询。


算法详解

线段树通用的build方法

void build(int k,int l,int r)
{
if(l==r)
{
sum1[k]=tree[l];//求和或者最值
sum2[k]=tree[l]*tree[l];//平方和
return;
}
int mid=(l+r)>>1;
build(ls(k),l,mid);
build(rs(k),mid+1,r);
pushup(k);//代表更新sum,这里可以是sum[k]=sum[(ls(k)]+sum[rs(k)]。
}

因为要进行区间的查询和修改,线段树还需要两个函数,check和update。check函数和update都用到了分治的思想。
简单的应用是区间修改+单点查询,区间查询+单点修改。
区间修改单点查询的思路是将区间标记,然后查询时从上到下加起来,直到叶子节点。区间查询+单点修改的思路是修改时直到叶子节点。单点的操作递归不会出现左右都有的情况,其余与区间操作类似。
下面的代码都是都是对区间进行操作的。

int check(int k,int l,int r,int x,int y)
{
if(l>=x&&r<=y)return sum[k];
int mid=(l+r)>>1;
int ans=0;
if(x<=mid)ans+=check(ls(k),l,mid,x,y);
if(y>mid)ans+=check(rs(k),mid+1,r,x,y);
return ans;
}

void ADD(int k,int l,int r,int x,int y,int v)
{
if(l>=x&&r<=y)
{
sum[k]+=v*(r-l+1);
return ;
}
int mid=(l+r)>>1;
if(x<=mid)ADD(ls(k),l,mid,x,y,v);
if(y>mid)ADD(rs(k),mid+1,r,x,y,v);
pushup(k);
}

算法进阶

线段树可以进行复杂的区间修改和区间查询操作,主要要用到pushdown函数和懒标记。
懒标记是修改的因子,在对某个节点进行操作时,如果不需要对其儿子节点进行操作,就尽在这个节点进行懒标记,如果后边会对儿子节点进行修改查询操作,就要pushdown,向下传递懒标记。还有一些特殊的根号线段树和除法线段树。


例题


P1471 方差


题意

题目要求维护区间并求区间的方差,通过化简可以得知,求解方差只需要维护区间平方和与区间和即可。


代码


int const maxn=1000005;
double sum1[maxn*2],sum2[maxn*2],tree[maxn],lazy[maxn*2];
inline int read()
{
char c=getchar();int x=0,f=1;
while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘;c=getchar();}
return x*f;
}
void pushup(int k)
{
sum1[k]=sum1[ls(k)]+sum1[rs(k)];
sum2[k]=sum2[ls(k)]+sum2[rs(k)];
}
void build(int k,int l,int r)
{
if(l==r)
{
sum1[k]=tree[l];
sum2[k]=tree[l]*tree[l];
return;
}
int mid=(l+r)>>1;
build(ls(k),l,mid);
build(rs(k),mid+1,r);
pushup(k);
}
void pushdown(int k,int l,int r)//pushdown的写法一般是线段树题目的核心
{
int mid=(l+r)>>1;
sum2[ls(k)]+=lazy[k]*lazy[k]*(mid-l+1)+2*lazy[k]*sum1[ls(k)];
sum2[rs(k)]+=lazy[k]*lazy[k]*(r-mid)+2*lazy[k]*sum1[rs(k)];
sum1[ls(k)]+=lazy[k]*(mid-l+1);
sum1[rs(k)]+=lazy[k]*(r-mid);

lazy[ls(k)]+=lazy[k];
lazy[rs(k)]+=lazy[k];
lazy[k]=0;
}
double check1(int k,int l,int r,int x,int y)
{
if(l>=x&&r<=y)return sum1[k];

if(lazy[k])pushdown(k,l,r);//关键代码

int mid=(l+r)>>1;
double ans=0;
if(x<=mid)ans+=check1(ls(k),l,mid,x,y);
if(y>mid)ans+=check1(rs(k),mid+1,r,x,y);
return ans;
}
double check2(int k,int l,int r,int x,int y)
{
if(l>=x&&r<=y)return sum2[k];

if(lazy[k])pushdown(k,l,r);//关键代码

int mid=(l+r)>>1;
double ans=0;
if(x<=mid)ans+=check2(ls(k),l,mid,x,y);
if(y>mid)ans+=check2(rs(k),mid+1,r,x,y);
return ans;
}
void ADD(int k,int l,int r,int x,int y,double v)
{
if(l>=x&&r<=y)
{
sum2[k]+=v*v*(r-l+1)+2*v*sum1[k];
sum1[k]+=v*(r-l+1);
lazy[k]+=v;
return ;
}

if(lazy[k])pushdown(k,l,r);//关键代码

int mid=(l+r)>>1;
if(x<=mid)ADD(ls(k),l,mid,x,y,v);
if(y>mid)ADD(rs(k),mid+1,r,x,y,v);
pushup(k);
}
main(void)
{
int n=read();
int m=read();
int x,y,cmd;
double k;
for(int i=1;i<=n;i++)
{
scanf("%lf",&tree[i]);
}
build(1,1,n);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&cmd,&x,&y);
if(cmd==1)
{
scanf("%lf",&k);
ADD(1,1,n,x,y,k);
}
if(cmd==2)
{
printf("%.4lf\n",check1(1,1,n,x,y)/(y-x+1));
}
if(cmd==3)
{
double as,pfs;
as=check1(1,1,n,x,y);
pfs=check2(1,1,n,x,y);
double l=y-x+1;
double ans=pfs/l-(as/l)*(as/l);
printf("%.4lf\n",ans);
}
}
}

P3373 【模板】线段树2


题意

维护区间的乘积和加法,两个懒标记,一个为乘积因子,一个为加法因子


代码


const int maxn=200010;
int p,a[maxn],sum[4*maxn],mul[4*maxn],add[4*maxn];
inline void qm1(int k)
{

sum[ls(k)]%=p;
mul[ls(k)]%=p;
add[ls(k)]%=p;

sum[rs(k)]%=p;
mul[rs(k)]%=p;
add[rs(k)]%=p;
}
inline void build(int k,int l,int r)
{
mul[k]=1;
if(l==r)
{
sum[k]=a[l];
return ;
}
int mid=(l+r)/2;
build(ls(k),l,mid);
build(rs(k),mid+1,r);
sum[k]=sum[ls(k)]+sum[rs(k)];
}
inline void pushdown(int k,int l,int r)
{
int mid=(l+r)/2;
sum[ls(k)]*=mul[k];
sum[rs(k)]*=mul[k];

sum[ls(k)]+=add[k]*(mid-l+1);
sum[rs(k)]+=add[k]*(r-mid);

mul[ls(k)]*=mul[k];
mul[rs(k)]*=mul[k];

add[rs(k)]=add[rs(k)]*mul[k]+add[k];
add[ls(k)]=add[ls(k)]*mul[k]+add[k];
qm1(k);
add[k]=0;
mul[k]=1;
}
inline void ADD(int k,int l,int r,int x,int y,int v)
{
if(x<=l&&y>=r)
{
add[k]+=v;
sum[k]+=v*(r-l+1);
qm1(k);
return ;
}
pushdown(k,l,r);
int mid=(l+r)/2;
if(x<=mid)ADD(ls(k),l,mid,x,y,v);
if(y>mid)ADD(rs(k),mid+1,r,x,y,v);
sum[k]=sum[ls(k)]+sum[rs(k)];
}
inline void MUL(int k,int l,int r,int x,int y,int v)
{
if(x<=l&&y>=r)
{
add[k]*=v;//规定的规则是先加后乘,想要表示先乘后加必须要把加法标记更新
mul[k]*=v;
sum[k]*=v;
qm1(k);
return ;
}
pushdown(k,l,r);
int mid=(l+r)/2;
if(x<=mid)MUL(ls(k),l,mid,x,y,v);
if(y>mid)MUL(rs(k),mid+1,r,x,y,v);
sum[k]=sum[ls(k)]+sum[rs(k)];
}
inline int check(int k,int l,int r,int x,int y)
{
if(x<=l&&y>=r)return sum[k];
pushdown(k,l,r);
int mid=(l+r)/2;
int ans=0;
if(x<=mid)ans+=check(ls(k),l,mid,x,y);
if(y>mid)ans+=check(rs(k),mid+1,r,x,y);
return ans%p;
}
main(void)
{
int n,m,cmd,x,y,k;
scanf("%lld%lld%lld",&n,&m,&p);
_1for(i,n)
{
scanf("%lld",&a[i]);
}
build(1,1,n);
_1for(j,m)
{
scanf("%lld%lld%lld",&cmd,&x,&y);
if(cmd==1)
{
scanf("%lld",&k);
MUL(1,1,n,x,y,k);
}
if(cmd==2)
{
scanf("%lld",&k);
ADD(1,1,n,x,y,k);
}
if(cmd==3)
{
printf("%lld\n",check(1,1,n,x,y)%p);
}
}
}

线段树入门到自闭



推荐阅读
  • egg实现登录鉴权(七):权限管理
    权限管理包含三部分:访问页面的权限,操作功能的权限和获取数据权限。页面权限:登录用户所属角色的可访问页面的权限功能权限:登录用户所属角色的可访问页面的操作权限数据权限:登录用户所属 ... [详细]
  • 本文提供了一个关于AC自动机(Aho-Corasick Algorithm)的详细解析与实现方法,特别针对P3796题目进行了深入探讨。文章不仅涵盖了AC自动机的基本概念,还重点讲解了如何通过构建失败指针(fail pointer)来提高字符串匹配效率。 ... [详细]
  • 本文介绍了一个来自AIZU ONLINE JUDGE平台的问题,即清洁机器人2.0。该问题来源于某次编程竞赛,涉及复杂的算法逻辑与实现技巧。 ... [详细]
  • 本文介绍了用户界面(User Interface, UI)的基本概念,以及在iOS应用程序中UIView及其子类的重要性和使用方式。文章详细探讨了UIView如何作为用户交互的核心组件,以及它与其他UI控件和业务逻辑的关系。 ... [详细]
  • 本报告记录了嵌入式软件设计课程中的第二次实验,主要探讨了使用KEIL V5开发环境和ST固件库进行GPIO控制及按键响应编程的方法。通过实际操作,加深了对嵌入式系统硬件接口编程的理解。 ... [详细]
  • LeetCode 102 - 二叉树层次遍历详解
    本文详细解析了LeetCode第102题——二叉树的层次遍历问题,提供了C++语言的实现代码,并对算法的核心思想和具体步骤进行了深入讲解。 ... [详细]
  • 深入解析Unity3D游戏开发中的音频播放技术
    在游戏开发中,音频播放是提升玩家沉浸感的关键因素之一。本文将探讨如何在Unity3D中高效地管理和播放不同类型的游戏音频,包括背景音乐和效果音效,并介绍实现这些功能的具体步骤。 ... [详细]
  • Java中提取字符串的最后一部分
    本文介绍了如何使用Java中的substring()和split()方法来提取字符串的最后一部分,特别是在处理包含特殊字符的路径时的方法与技巧。 ... [详细]
  • 本文探讨了线性表中元素的删除方法,包括顺序表和链表的不同实现策略,以及这些策略在实际应用中的性能分析。 ... [详细]
  • 本文由chszs撰写,详细介绍了Apache Mina框架的核心开发流程及自定义协议处理方法。文章涵盖从创建IoService实例到协议编解码的具体步骤,适合希望深入了解Mina框架应用的开发者。 ... [详细]
  • C/C++ 应用程序的安装与卸载解决方案
    本文介绍了如何使用Inno Setup来创建C/C++应用程序的安装程序,包括自动检测并安装所需的运行库,确保应用能够顺利安装和卸载。 ... [详细]
  • Awk是一款功能强大的文本分析与处理工具,尤其在数据解析和报告生成方面表现突出。它通过读取由换行符分隔的记录,并按照指定的字段分隔符来划分和处理这些记录,从而实现复杂的数据操作。 ... [详细]
  • 本文探讨了一种常见的C++面试题目——实现自己的String类。通过此过程,不仅能够检验开发者对C++基础知识的掌握程度,还能加深对其高级特性的理解。文章详细介绍了如何实现基本的功能,如构造函数、析构函数、拷贝构造函数及赋值运算符重载等。 ... [详细]
  • 随着Linux操作系统的广泛使用,确保用户账户及系统安全变得尤为重要。用户密码的复杂性直接关系到系统的整体安全性。本文将详细介绍如何在CentOS服务器上自定义密码规则,以增强系统的安全性。 ... [详细]
  • Python3爬虫入门:pyspider的基本使用[python爬虫入门]
    Python学习网有大量免费的Python入门教程,欢迎大家来学习。本文主要通过爬取去哪儿网的旅游攻略来给大家介绍pyspid ... [详细]
author-avatar
红箭777_387
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有