线段树

可持续化线段树(例题Sign on Fence[Codeforces 484E])

 ̄綄美尐妖づ 提交于 2020-01-02 12:09:23
刚刚学习的想记录一下: 第一次接触可持续化线段树,很懵。。。 题目: 题目描述 izon the Champion has recently finished painting his wood fence. The fence consists of a sequence of n panels of 1 meter width and of arbitrary height. The i -th panel's height is h i meters. The adjacent planks follow without a gap between them. After Bizon painted the fence he decided to put a "for sale" sign on it.The sign will be drawn on a rectangular piece of paper and placed on the fence so that the sides of the sign are parallel to the fence panels and are also aligned with the edges of some panels. Bizon the Champion introduced the following

[BZOJ]2653: middle

こ雲淡風輕ζ 提交于 2020-01-02 12:09:05
Time Limit: 20 Sec Memory Limit: 512 MB Description   一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。给你一个长度为n的序列s。回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的连续子序列中,最大的中位数。其中a<b<c<d。位置也从0开始标号。我会使用一些方式强制你在线。 Input   第一行序列长度n。接下来n行按顺序给出a中的数。   接下来一行Q。然后Q行每行a,b,c,d,我们令上个询问的答案是x(如果这是第一个询问则x=0)。   令数组q={(a+x)%n,(b+x)%n,(c+x)%n,(d+x)%n}。   将q从小到大排序之后,令真正的要询问的a=q[0],b=q[1],c=q[2],d=q[3]。   输入保证满足条件。 Output   Q行依次给出询问的答案。 Sample Input   5   170337785   271451044   22430280   969056313   206452321   3   3 1 0 2   2 3 1 4   3 1 4 0 Sample Output   271451044   271451044   969056313 HINT   0:n,Q<=100   1,..

Codeforces 1132G(dfs序+线段树)

梦想的初衷 提交于 2020-01-02 12:08:11
题面 传送门 分析 对于每一个数a[i],找到它后面第一个大于它的数a[p],由p向i连边,最终我们就会得到一个森林,且p是i的父亲。为了方便操作,我们再增加一个虚拟节点n+1,把森林变成树。 由于序列不是递增的,不能二分。维护一个单调栈,栈顶元素最小。从n到1依次对每个 数操作,弹出栈里比它小的数。如果栈为空,说明该数是森林中的根节点,向n+1连边。否则栈顶元素就是第一个大于它的数,向它的编号连边即可。 我们发现,对于每个查询区间内的所有数,它对应着树上的某些节点,记为标记节点。如果把标记节点之间的非标记节点去掉,我们就会得到一棵新树,新树上从某个节点到根的一条路径对应着一个满足条件的序列,则最大序列长度等于新树上从叶子节点到根的最长路径。这样,我们就把问题转化为了树上的最长路径。 显然不能对每一个询问建一棵新树。我们发现新树上的路径长度就是原树上的路径经过的标记节点个数,如图(加粗的节点为标记节点)。 所以,我们建立一棵线段树,线段树的叶子节点存储原树上每个节点到根的路径上的标记节点个数,线段树维护最大值。 我们枚举每个长度为k的区间[i,i+k-1],显然从前一个区间转移到当前区间时,只会增加一个标记节点,减少一个标记节点。每增加一个标记节点i,我们就将i的子树内的所有节点的值+1,否则-1。答案即为整颗线段树的最大值 时间复杂度 \(O(n\log n)\) 代码

洛谷 P2617 Dynamic Rankings || ZOJ - 2112

别来无恙 提交于 2020-01-02 12:06:57
写的让人看不懂,仅留作笔记 静态主席树,相当于前缀和套(可持久化方法构建的)值域线段树。 建树方法:记录前缀和的各位置的线段树的root。先建一个"第0棵线段树",是完整的(不需要用可持久化的方法),所有数据为0。后面每一个位置的前缀和放的线段树都先设root与前一位置的线段树一样,然后再按照原序列在指定位置进行(可持久化的)单点加法。(类比放数值的前缀和,每一位置的前缀和是前一位置前缀和加上当前位置数值) 带修改主席树,相当于区间树状数组套(可持久化方法构建的)值域线段树。 建树方法:记录树状数组各位置线段树的root。先建一个"第0棵线段树",是完整的,按普通线段树建,所有数据为0。一开始设所有root都为第0棵线段树的根。对于原序列某位置的值,相当于在树状数组某位置进行一次单点加法。 可持久化方法建线段树:每一个节点进行操作时,都先把原来的节点复制一份(指同时复制 左右 孩子 (以前错在只复制了一个孩子) 的 位置 和数据,但不递归复制左右孩子),再进行更新等操作。这样的话每一次单点更新操作只会新建logn个节点(因为每次单点更新只会路过这么多点),(相比一般的树套树:外层树每一个点放一个完整的内层树)可以节省空间。 由于是值域线段树,都需要做离散化。 这么做,就可以:在log^2n的时间内,完成一次查询"某区间内小于等于某数的数的个数"的操作。(对于区间[l,r]

线段树+ dp

倾然丶 夕夏残阳落幕 提交于 2020-01-01 00:18:33
传送门 include include define ll long long define lson l,m,rt<<1//左儿子 define rson m+1,r,rt<<1|1//右儿子 define inf 0x3f3f3f3f using namespace std; const int maxn = 1e5 + 5; int n,m,sum[maxn<<2],tree[maxn<<2]; int k; int c[maxn],dp[maxn]; int ans = 0; int w[maxn]; int R,L,C; int read(){//快速读入数字 int a=0;char x=getchar();bool f=0; while((x<'0'||x>'9')&&x!='-')x=getchar(); if(x=='-')x=getchar(),f=1; while(x>='0'&&x<='9')a=a*10+x-48,x=getchar(); return f?-a:a; } void pushup(int rt){ sum[rt] = min(sum[rt<<1], sum[rt<<1|1]); } void pushdown(int rt){ sum[rt<<1] = min(sum[rt<<1],tree[rt]); sum[rt<<1|1] = min

POJ3321 - Apple Tree DFS序 + 线段树或树状数组

微笑、不失礼 提交于 2019-12-27 05:10:35
Apple Tree: http://poj.org/problem?id=3321 题意:   告诉你一棵树,每棵树开始每个点上都有一个苹果,有两种操作,一种是计算以x为根的树上有几个苹果,一种是转换x这个点上的苹果,就是有就去掉,没有就加上。 思路:   先对树求一遍dfs序,每个点保存一个l,r。l是最早到这个点的时间戳,r是这个点子树中的最大时间戳,这样就转化为区间问题,可以用树状数组,或线段树维护区间的和。 #include <algorithm> #include <iterator> #include <iostream> #include <cstring> #include <cstdlib> #include <iomanip> #include <bitset> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <stack> #include <cmath> #include <queue> #include <list> #include <map> #include <set> #include <cassert> using namespace std; //#pragma GCC optimize(3) //#pragma comment

hdu 5039 线段树+dfs序

微笑、不失礼 提交于 2019-12-27 05:08:34
http://acm.hdu.edu.cn/showproblem.php?pid=5039 给定一棵树,边权为0/1。m个操作支持翻转一条边的权值或者询问树上有多少条路径的边权和为奇数。 用树形dfs出每个点到根的路径上边权和是否为奇数; 由于翻转一个边只会连带影响其下的子节点,所有线段树记录更新区间,odd记录到根的路径上边权和为奇数的个数,每次只需更新和查询odd,没有lazy会超时 #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <string> #include <queue> #include <map> #include <iostream> #include <algorithm> using namespace std; #define RD(x) scanf("%d",&x) #define RD2(x,y) scanf("%d%d",&x,&y) #define RD3(x,y,z) scanf("%d%d%d",&x,&y,&z) #define clr0(x) memset(x,0,sizeof(x)) typedef long long LL; #define L(x) (x<<1) #define R(x) ((x<<1)|1)

[IOI2018]排座位——线段树

一世执手 提交于 2019-12-26 22:38:55
题目链接: IOI2018seat 题目大意:给出一个$H*W$的矩阵,将$0 \sim W*H-1$分别填入矩阵的格子里(每个格子里一个数),定义一个子矩阵是美妙的当且仅当这个子矩阵包含且仅包含$0 \sim i$($i$为$0 \sim W*H-1$的任意数)这些数,每次调换两个数的位置,求有多少个美妙的子矩阵。 这题思路真的很神。 原题编号从0开始,很不舒服,我们按从1开始的讲。 发现只需要判断[1,i]这些数是否组成了一个矩阵。 那么我们能不能用线段树,第i个叶子节点存前i个数的信息来判断前i个数能否组成矩阵呢? 有的人可能会想到第i个叶子节点维护前i个数中最左上的点和最右下的点,判断时直接取这两个点形成矩形的面积看是否等于i。 这个判断是可行了,但修改呢?你会发现交换两个点的位置会改变好多点的信息,甚至影响的信息达到O(n)级别。 这时真正的神仙操作来了。 在判断前i个点是否成立时我们将前i个点染成黑色,其他点为白色。 我们维护两个信息: 1、有多少黑点的左边和上边都是白点或边界 2、有多少白点的四联通块中包含大于等于2个黑点 可以看出,如果前i个点形成矩形那么第一个信息值为1,第二个信息值为0。同理也只有这种情况才是矩形。 为什么呢? 如果黑点都连在一起形成一个图形,那么第二个信息为0保证他是一个凸多边形且多边形的边与整个图是平行的, 而第一个信息为1则保证他有四个顶点

数据结构——线段树与树状数组

一曲冷凌霜 提交于 2019-12-25 00:52:24
写在前面 貌似是我这个菜文鸡第一次写总结。 一咕再咕的我总算滚回来学线段树和树状数组啦, 然而此时身边大佬早已过了紫荆花之恋。 看了一堆网上的文章和高二大佬们留下来的书,写下来方便以后复习。 引入 给出n个数,再给出m次操作,操作包含 1.求出区间[l,r]的最大值(区间查询) 2.求出第k个数的值(单点查询) 3.给区间[l,r]增加一个值x(区间修改) 4.给第k个数加上一个值x(单点修改) 如果n*m«100000000,对于每次询问就可以O(n)暴力出奇迹。 但如果n*m≥ 100000000 ,想打暴力的同学恐怕就要自闭分离了。 这个时候,就需要数据结构来维护我们得到的信息。 (问:什么是数据结构?众大佬: 就是在考场上没人打得出来的毒瘤。 ) 我们来看看两个相对简单的数据结构—线段树和树状数组 正文 线段树 这里是度娘给出的定义“ 线段树是一种二叉索引树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点 ”。 显然,大家看完这句话后应该都是懵逼的。 线段树,顾名思义,就是线段(区间)的树。而这棵树一般是二叉树(如图)。 如果把字母换成一段线段(区间)[l,r],节点左右儿子为区间的中点隔开的左右子区间[l,mid]和[mid+1,r],这棵二叉树就是线段树。Just like this。 或者当数字不是这么好看的时候就会变成这样:

主席树——多棵线段树的集合

眉间皱痕 提交于 2019-12-22 12:13:45
主席树: (不要管名字) 我们有的时候,会遇到很多种情况,对于每一种情况,都需要通过线段树的操作实现。 碰巧的是,相邻两种情况下的线段树的差异不大。(总体的差异次数是O(N)级别的,均摊就是O(常数)的了) 显然的是,我们不能对于每种情况都建造一棵线段树。n^n 空间直接MLE无疑。 救命稻草是:发现相邻两种情况下的线段树的差异不大。 所以,我们是否可以让不同的线段树共用同一个节点呢?!?!? 这就是主席树的本质。也是精妙之处所在。 代码实现不是很麻烦。 我一般用传返回值形式,每次返回一个节点编号,便于设置儿子编号。比较方便。 注意的是,我们必须记录lson,rson,不能采用x<<1,x<<1|1的形式。因为没有这样的规律可循。 你不知道子节点和自己有什么关系。(这是谁家的孩子?公家的) 经典例题: 1.区间第k小(大)。 离散化必须的。 对于每一个区间节点开一个权值线段树 。i的线段树的节点l~r表示,在真正的区间1~i中,大小在l~r的数出现的次数。 记录每个线段树节点根的所在位置。 查询的时候,l-1,r两棵线段树同时出发,区间[a,b]sum值做一个差,就是l~r这个区间内,数值在[a,b]之间的数的个数。 对于区间第k小,选择左儿子区间做差,u<k,就进入右儿子,同时k-=u 否则进入左儿子。 区间第k大正相反。 对于n棵主席树,相邻两个主席树i,i