线段树

线段树、树状数组入门

|▌冷眼眸甩不掉的悲伤 提交于 2019-11-27 19:09:30
HDU-1166 思路:更新区间,结构体内保存最大值 1 #include<stdio.h> 2 #include<string> 3 #include<iostream> 4 using namespace std; 5 const int MAXN = 5e4+5; 6 int a[MAXN]; 7 struct node 8 { 9 int L,R,sum,la; 10 }tree[MAXN<<2]; 11 //建树 12 void Build(int L,int R,int step) 13 { 14 tree[step].L=L;tree[step].R=R; 15 if(L==R) 16 { 17 tree[step].sum=a[L];return; 18 } 19 int mid=(L+R)>>1; 20 Build(L,mid,step<<1); 21 Build(mid+1,R,step<<1|1); 22 tree[step].sum=tree[step<<1].sum+tree[step<<1|1].sum; 23 } 24 //单点查询 25 int Query(int P,int step) 26 { 27 if(tree[step].L==tree[step].R)return tree[step].sum; 28 int mid=(tree[step

李超线段树学习笔记

情到浓时终转凉″ 提交于 2019-11-27 18:56:57
作用 支持在二维平面上插入一个线段,查询覆盖横坐标 \(x\) 的所有线段的 \(y\) 的最大/最小值。 如果是直线(即覆盖整个平面),那么是 \(O(\log n)\) ,否则是 \(O(\log^2n)\) 算法思想 对于线段树每个区间,保存其“最优势线段”。定义为在 mid 处取最值的区间。可以证明求答案时答案一定取在从根到叶子的路径上的所有“最优势线段”。 每次来一个新的线段时,和旧线段比较,如果更优势那么替换掉,否则尝试继续向下递归覆盖。比如新线段和旧线段的交点在mid左边,那么就用剩下的一个线段去递归左区间。 好的证明比较难,感性理解一下的话又似乎很对,所以就不证了…… 关于复杂度:首先定位对应区间,再对每个区间尝试向下递归覆盖,后者因为每次递归一边所以至多log次。总复杂度 \(O(\log^2 n)\) 这个东西最厉害的地方在一些关于凸包/一次函数的题目非常无脑。并且李超线段树也是一个维护凸包的有力工具。 例题 louguP4097 裸题。实际上写成y=kx+b的形式会更短也更快,但是这个题的坐标范围比较大,理论上是会爆精度过不了的…… // luogu-judger-enable-o2 #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef long double LD

[bzoj4025] 二分图

淺唱寂寞╮ 提交于 2019-11-27 18:20:59
Description 神犇有一个 \(n\) 个节点的图。因为神犇是神犇,所以在T时间内一些边会出现后消失。神犇要求出每一时间段内这个图是否是二分图。这么简单的问题神犇当然会做了,于是他想考考你。 Input 输入数据的第一行是三个整数 \(n\) , \(m\) , \(T\) 。 第2行到第 \(m+1\) 行,每行 4 个整数 \(u\) , \(v\) , \(start\) , \(end\) 。第 \(i+1\) 行的四个整数表示第 \(i\) 条边连接 \(u\) , \(v\) 两个点,这条边在 \(start\) 时刻出现,在第 \(end\) 时刻消失。 Output 输出包含 \(T\) 行。在第i行中,如果第 \(i\) 时间段内这个图是二分图,那么输出 “ \(Yes\) ”,否则输出“ \(No\) ”,不含引号。 Sample Input 3 3 3 1 2 0 2 2 3 0 3 1 3 1 2 Sample Output Yes No Yes HINT 样例说明: 0时刻,出现两条边1-2和2-3。 第1时间段内,这个图是二分图,输出 \(Yes\) 。 1时刻,出现一条边1-3。 第2时间段内,这个图不是二分图,输出 \(No\) 。 2时刻,1-2和1-3两条边消失。 第3时间段内,只有一条边2-3,这个图是二分图,输出 \(Yes\) 。

区间连续长度的线段树——洛谷P2894 [USACO08FEB]酒店Hotel

拜拜、爱过 提交于 2019-11-27 18:13:59
https://www.luogu.org/problem/P2894 #include<cstdio> #include<iostream> using namespace std; struct ben { int lmax,rmax,len,sum,lz; }tr[400005]; void bt(int x,int l,int r)//建树 { tr[x].lz=0;//标记清空 tr[x].sum=tr[x].len=tr[x].lmax=tr[x].rmax=r-l+1;//区间连续,区间长度,左端点连续,右端点连续都初始化成区间的长度。(因为所有房间都是空的 if(l==r)//如果到了叶子节点 { return ;//返回 } int mid=(l+r)/2; bt(x*2,l,mid); bt(x*2+1,mid+1,r); //左右建子节点 } void down(int x)//标记下放 { if(tr[x].lz==0)return ;//如果没有标记,直接返回 if(tr[x].lz==1)//如果是要开房 { tr[x*2].lz=tr[x*2+1].lz=1;//左右子节点都要标记上开房 tr[x*2].rmax=tr[x*2].lmax=tr[x*2].sum=0; tr[x*2+1].rmax=tr[x*2+1].lmax=tr[x*2+1]

楼房重建(分块/线段树)

江枫思渺然 提交于 2019-11-27 16:54:25
问题: 小 A 的楼房外有一大片施工工地,工地上有 N 栋待建的楼房。每天,这片工地上的房子拆了又建、建了又拆。他经常无聊地看着窗外发呆,数自己能够看到多少栋房子。 为了简化问题,我们考虑这些事件发生在一个二维平面上。小 A A 在平面上 (0,0) 点的位置,第 i i 栋楼房可以用一条连接 (i,0) 和 ( i , H i ​ ) 的线段表示,其中 H i ​ 为第 i 栋楼房的高度。 如果这栋楼房上存在一个高度大于 0 的点与 (0,0) 的连线没有与之前的线段相交,那么这栋楼房就被认为是可见的。 施工队的建造总共进行了 M 天。初始时,所有楼房都还没有开始建造,它们的高度均为 0 。 在第 i 天,建筑队将会将横坐标为 X i ​ 的房屋的高度变为 Y i ​ (高度可以比原来大—修建,也可以比原来小—拆除,甚至可以保持不变—建筑队这天什么事也没做)。 请你帮小 A 数数每天在建筑队完工之后,他能看到多少栋楼房? 解 : 注意一些点 如果是要求修改的 绝大部分是数据结构 不要再往逆序对上想了啊! $1$ 线段树 分治 分而治之 cnt代表当前区间内可行的总数 code: // #include<stdio.h> #include<bits/stdc++.h> using namespace std; #define maxnn 700000 int n,m; struct

hdu-1540(线段树+二分)

北慕城南 提交于 2019-11-27 16:21:15
问题:在抗日战争期间,华北平原广大地区进行了大规模的隧道战。 一般来说,通过隧道连接的村庄排成一列。 除了两端,每个村庄都与两个相邻的村庄直接相连。 入侵者经常对一些村庄发动袭击并摧毁其中的部分隧道。 八路军指挥官要求最新的隧道和村庄连接状态。 如果某些村庄严重隔离,必须立即恢复连接! 输入:输入的第一行包含两个正整数n和m(n,m≤50,000),表示村庄和事件的数量。 接下来的m行中的每一行描述一个事件。以下所示的不同格式描述了三种不同的事件: D x:第x个村庄被毁。 Q x:指挥官询问第x个村庄与其直接或间接相关的村庄数量。 R:最后毁坏的村庄被重建了。 输出:按顺序输出每个指挥官询问的答案。 分析:由于刚刚接触这一题的时候,我并不知道线段树维护连续区间这个东西,我昨完后看题解发现很多都是这个写法,但是这里我就提供一下我自己的一个思路。 我也是建线段树,但是我的线段树只维护了一个东西,就是这个区间是不是都被摧毁,或者都安全,比如村庄1存活,村庄2被摧毁,那么线段树区间对应的【1,1】,就应该是1(表示这段区间的房子存活),【2,2】就应该是0(表示这段区间的房子被摧毁),【1,2】应该是0,(表示这段区间没有全都存活)。 那么对于问题我们如果解决?我是这么分析的,由于是判断这个村庄左右存活的连续村庄与该村庄是否相连,那么就对该询问村庄的左右两边进行各进行一次二分查找答案。

线段树模板知识整理(懒标记、结构体)

与世无争的帅哥 提交于 2019-11-27 15:47:09
一套适合自己风格的线段树模板整理 经过一天半的煎熬,刚开始头疼的不行,实在想不通,到现在线段树终于像是入坑了,在网上找了好多资料,终于能结合人家的写一套适合我风格的模板。现在来记录下我的一些理解和代码,在以后含糊的时候返回来看看。 推荐视频 :b站 正月点灯笼 思路很容易理解,但代码难模仿,SWPU-ACM的代码还是容易模仿的。还有强大的CSDN上有很多详细的讲解。 我的代码主要时使用一个结构体代表树,我觉得用结构体能够更好地展示这个树的特点,和每一个结点的内容。在写的过程中保存了好多测试截图的,不过现在懒得上传了。。。该有的注释我加到代码旁边。 以下函数包括: void build ( int rt , int l , int r ) (建树) rt代表每次要创建的根节点,l和r为这个树上表示的左右端点值 void update ( int rt , int p , int val ) (单点修改) rt仍然为树的根节点,p为要修改的位置,val为新值 void update ( int rt , int l , int r , int c ) (区间修改) 此函数作用为在l到r这一区间内,对每个值增加c int query ( int rt , int p ) (单点查询) 查询p点的值 int query ( int rt , int l , int r ) (区间查询)

P3372 【模板】线段树 1

笑着哭i 提交于 2019-11-27 15:45:01
#include<cstdio> using namespace std; struct ben { long long l,r,val,mark; }tr[400005]; long long a[100005]; void bt(long long x,long long l,long long r) { tr[x].l=l; tr[x].r=r; if(l==r) { tr[x].val=a[l]; return ; } long long lch=x*2; long long rch=x*2+1; long long mid=(l+r)/2; bt(lch,l,mid); bt(rch,mid+1,r); tr[x].val=tr[lch].val+tr[rch].val; } void Down(long long x)//标记下传 { if(tr[x].mark&&tr[x].l<tr[x].r) { long long lch=x*2; long long rch=x*2+1; tr[lch].val+=tr[x].mark*(tr[lch].r-tr[lch].l+1); tr[lch].mark+=tr[x].mark; tr[rch].val+=tr[x].mark*(tr[rch].r-tr[rch].l+1); tr[rch].mark+=tr[x]

【算法•日更•第四十五期】静态二叉排序树(建立)

蹲街弑〆低调 提交于 2019-11-27 15:35:47
▎前言   小编虽然已经会了二叉排序树,但是却不明白静态二叉排序树和二叉排序树有什么关系。   结合平衡二叉排序树(例如:红黑树)食用本篇博客,效果更佳哦~    传送门 (同时也内含二叉排序树的相关知识) ▎静态二叉排序树 ☞ 『引入』   这个东西要从线段树说起 ,别问我为什么扯这么远 。    戳这里快速上手线段树 。   不得不说,线段树是个好东西,每次都会把一个区间劈成左右两半,运用了分治的思想。   但是线段树更多的是用来处理区间类的问题,在遇到数据点统计问题的时候,也能算出来,不过多保留了一下一些冗余的东西。   比如说:线段树总是定义过多的l,r,mid指针来指向区间,这就是冗余信息;   再比如说:线段树的节点是用来存储区间的,而在处理点的问题时,是不需要的,这就是冗余信息。   还有:在处理点时,线段树的区间一分为二,是通过一个分割点的,只要保留这个分割点就可以了。   像这样的冗余信息不止这些,但是如果我们换成二叉排序树就不同了。 ☞ 『定义』   个人认为二叉排序树和静态二叉排序树在定义上没有区别。 ☞ 『特点』   用例子来说吧:   比如说现在有这样一组数据:3,6,9,2,5,4,1。   先明确一下位置关系:      假设当前节点为i,那么左子树编号为i×2,右子树编号为i×2+1,所以我们用数组进行存储的时候就是按照这个规律来存储的。  

可持续化线段树

半腔热情 提交于 2019-11-27 15:32:54
模板 单点修改单点查询 #include <cstdio> using namespace std; const int maxn=2e7+5e6; const int maxm=1e6+5; int v[maxn],lson[maxn],rson[maxn]; int root[maxm]={1},a[maxm];//root[i]存i版本对应根节点(0号版本对应根为1) int tot; int build(int l,int r){ int pos=++tot; if(l==r){ v[pos]=a[l]; return pos; } int mid=(l+r)>>1; lson[pos]=build(l,mid); rson[pos]=build(mid+1,r); return pos; } int update(int rt,int l,int r,int p,int w){ int pos=++tot; if(l==r){ v[pos]=w; return pos; } lson[pos]=lson[rt]; rson[pos]=rson[rt]; int mid=(l+r)>>1; if(p<=mid) lson[pos]=update(lson[rt],l,mid,p,w); else rson[pos]=update(rson[rt],mid+1,r,p,w);