线段树

线段树整理

笑着哭i 提交于 2020-01-18 09:05:37
仅用于个人复习 有问题还望指出 线段树 是一个基于二分思想的一种二叉搜索树 能在 log2(n) 的时间内 完成更新和查找操作 其中 线段树有以下操作 1.建树 线段树的模板大都是固定的 最为重要的是 应该明确用线段树来维护什么信息 也就是你得到答案需要的信息 明确了这个信息之后 之后的操作就会显得行云流水 struct node ///结构体保存信息 { int l , r , ans ; } t [ maxn * 4 ] ; void build ( int v , int l , int r ) { t [ v ] . l = l ; t [ v ] . r = r ; if ( l == r ) { scanf ( "%d" , & t [ v ] . ans ) ; return ; } int mid = ( l + r ) / 2 ; build ( 2 * v , l , mid ) ; //建立左子树 build ( 2 * v + 1 , mid + 1 , r ) ; //右子树 t [ v ] . ans = max ( t [ v * 2 ] . ans , t [ v * 2 + 1 ] . ans ) ; ///更新最大值 } 左子树和右子树 也可以使用位运算的版本 分别为 v<<1 和 v<<1|1 线段树的内存一般要开大4倍左右

一道线段树练习题

落花浮王杯 提交于 2020-01-18 01:30:22
题意 解法 首先考虑一个 O ( n 2 ) O(n^2) O ( n 2 ) 的解法,我们先考虑求出不含任何星星的矩形个数,这个可以考虑单调栈做法,具体而言,对于每个右下角,它的左上角形成的图形是一个台阶的形状,然后就枚举每一行,然后首先计算每个位置出现星星的最晚时间,用单调栈维护每个位置向后第一个出现星星时间比当前时间晚的位置,转移的时候对一些区间加等差数列. 然后考虑更快的做法:将单调栈换成线段树,然后维护同样的问题.我第一次写的时候想一颗线段树直接把单调栈维护出来,但是定义的很有问题,导致其不单调了.所以最后还是用一棵线段树维护区间min,另一棵线段树维护当前行的情况. 最后用总数减去这些不合法的就好了. # include <bits/stdc++.h> using namespace std ; # define int long long const int maxn = 7e4 + 5 ; inline char get_char ( ) { //超级快读 static char buf [ 1000001 ] , * p1 = buf , * p2 = buf ; return p1 == p2 && ( p2 = ( p1 = buf ) + fread ( buf , 1 , 1000000 , stdin ) , p1 == p2 ) ? EOF : *

「考试」省选8

淺唱寂寞╮ 提交于 2020-01-16 07:04:27
T1一看是毒瘤分块根本不想写。 T2部分分都想不到。 T3只会打暴力。 T1 正解是比较有意思的扫描线+线段树维护单调栈。 如果我们把操作序列写出来一定是这个样子的。 \(M,AAA,M,AA,MM,A\) 然后如果我们把其中的加法操作全部累和就是: \(A_i,M_i,A_j,M_j,A_k,M_k\) 这样的话再设 \(S_i=\sum\limits_{j=1}^{i}A_j\) 。 这样一个新加入的对 \(\{A_i,M_i\}\) ,能够加入栈中的条件就是: \(A_tp+S_i-S_tp<M_i\) 那么也就是: \(\{A_i-S_i\}\) 这个序列的上升序列。 可以用线段树维护单调栈维护。 对于某一个位置按操作序列维护出单调栈。 从1到询问时间点的单调栈长度+此时从1到询问时间点的加法标记个数就是值改变的次数。 我们考虑用线段树维护单调栈来做这一过程。 然后再利用扫描线算法。 从左到右,差分询问的区间,线段树上同时支持加入和删除某一个点,区间加和区间求单调栈长度,而同时支持单点修改。 这样对于某一个位置来说,我们先在时间轴上加入所有的覆盖这个点的操作,查询的时候查询时间轴上询问以前的全部区间就可以了。 然后我们可以得到单调栈最后一个元素的大小。 他的大小是 \(M_i-S_i\) ,这样再用一个树装数组就行了,查询加法操作的前缀和即可。 T2 结论题,不是很能想出来

寒假集训第三天---线段树部分题解

和自甴很熟 提交于 2020-01-16 03:04:02
CF1268B - Domino for Young 题目大意 :给您一排高度从左到右非递增的矩阵,输入为n(1≤n≤300000),代表有n列矩形;接下来一行有n个数字,代表对应矩阵高度(1≤ai≤300000,ai≥ai+1)。问您往里面插1 2或者2 1的矩阵最多能插多少个。 解法 :贪心考虑每一列,如果是偶数,直接填满1*2,如果是奇数,记录当前列。我们画图可得,如果两个最接近的高度为奇数的列,如果 中间有偶数个高度为偶数的矩阵 ,那都可以消掉(画图很明显)。如果它们之间只有 奇数个高度为偶数的矩阵 ,那怎么也不能消完,最少也会剩下两个点,即两个高度为奇数的矩阵的最下方的两个点( 贪心的考虑,留底下比留上面更优 )。按照这个思路一直贪心下去就可以了。 代 🐎 码 # include <bits/stdc++.h> using namespace std ; # define ll long long int main ( int argc , char const * argv [ ] ) { ll ans = 0 ; int n ; stack < int > st ; while ( ! st . empty ( ) ) st . pop ( ) ; scanf ( "%d" , & n ) ; for ( int i = 1 , x ; i <= n ; ++ i )

51nod 1559 车和矩形

给你一囗甜甜゛ 提交于 2020-01-15 21:49:26
http://www.51nod.com/Challenge/Problem.html#problemId=1559 倘若矩形是受保护的,那么矩形内每一行至少有一个车或者每一列至少有一个车 判断矩形内每一列都有一个车: 线段树中维护x坐标这一列车的最大y坐标 那么扫描线扫过矩形的上边界时 如果矩形左右边界内,车的最大y坐标中最小的那个大于等于矩形的下边界 那么这个矩形的每一列都有一个车 将车按y坐标从小到大排序,每次扫到一条矩形的上边界, 将y坐标<=这一上边界的车都加入线段树,即用车的y坐标更新线段树 即可实现这一过程 判断矩形每一行都有一个车同理 #include<cstdio> #include<iostream> #include<algorithm> using namespace std; #define N 100002 struct car { int x,y; }g[N<<1]; struct line { int li,ri,ui,di,id; bool ok; }e[N<<1]; int mi[N<<2],tmp; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }

【线段树 树链剖分 差分 经典技巧】loj#3046. 「ZJOI2019」语言【未完】

吃可爱长大的小学妹 提交于 2020-01-15 21:36:41
还是来致敬一下那过往吧 题目分析 先丢代码 1 #include<bits/stdc++.h> 2 const int maxn = 100035; 3 const int maxm = 200035; 4 const int maxNode = 20000035; 5 6 struct node 7 { 8 int top,son,fa,tot; 9 }a[maxn]; 10 struct point 11 { 12 int u,v; 13 point(int a=0, int b=0):u(a),v(b) {} 14 }; 15 struct tree 16 { 17 int ls,rs,cov,val; 18 }f[maxNode]; 19 int n,m,tot; 20 long long ans,det; 21 int chain[maxn],chTot,rt[maxn]; 22 int edgeTot,head[maxn],edges[maxm],nxt[maxm],dep[maxn]; 23 std::vector<point> opt[maxn]; 24 std::vector<int> inc[maxn],dec[maxn]; 25 26 int read() 27 { 28 char ch = getchar(); 29 int num = 0, fl = 1;

hdu 4616 Vases and Flowers 线段树

五迷三道 提交于 2020-01-15 12:48:05
sum[k]表示对应区间内部,空的花瓶的数量。 对于插花,那么从A开始插F朵花,就是找到从A开始时第一个1和第F个1,然后把这一段全部改0。注意到可能插到结尾花还多,所以先求A到n的空瓶数量和F取个min。 对于拿花,直接把对应区间全部赋值1就行了。 如何找到第rk个1,需要都带一个log,可以在外面调用二分,也可以在内部调用区间求和来判断往左往右向哪走。 lzy表示-1才表示没有lzy,这个细节出错调了一会... 1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 typedef long long ll; 5 int n,m,T; 6 ll sum[210000],lzy[210000]; 7 void build(int k,int l,int r) 8 { 9 lzy[k] = -1; 10 if (l == r) 11 { 12 sum[k] = 1; 13 return; 14 } 15 int mid = l + r >> 1; 16 build(k << 1,l,mid); 17 build(k << 1 | 1,mid + 1,r); 18 sum[k] = sum[k << 1] + sum[k << 1 | 1]; 19 } 20 void down(int k,int l

POJ 1151 Atlantis 线段树扫描线

♀尐吖头ヾ 提交于 2020-01-15 04:44:40
$ \Rightarrow $ 戳我进POJ原题 Atlantis Time Limit: 1000MS $ \quad $ Memory Limit: 10000K   Description There are several ancient Greek texts that contain descriptions of the fabled island Atlantis. Some of these texts even include maps of parts of the island. But unfortunately, these maps describe different regions of Atlantis. Your friend Bill has to know the total area for which maps exist. You (unwisely) volunteered to write a program that calculates this quantity.   Input The input consists of several test cases. Each test case starts with a line containing a single integer $ n (1 \le n \le 100

ZOJ 2301/HDU 1199 线段树+离散化

三世轮回 提交于 2020-01-15 04:44:25
给这个题目跪了两天了,想吐简直 发现自己离散化没学好 包括前一个离散化的题目,实际上是错了,我看了sha崽的博客后才知道,POJ那题简直数据弱爆了,本来随便一组就能让我WA掉的,原因在于离散化的时候,缩小数据规模的同时,没有考虑到误差,比如 1 4 6 8,离散化之后是1 2 3 4,我如果覆盖了1 2和3 4,表面上好像全部覆盖了,实际数据并没有覆盖。。所以我只能说那道题目我其实错了,并没有真正做出离散化出来。。。现在用这道题来弥补。 color the ball,ball的编号可以从1 到2^31,不可能开这么大线段树,但是其实测试数据只有N=2000*2组,所以必然是离散化,初始的时候,所有球都是黑色的,然后N组数组,a b w(b),说明把 区间 a到b染成(white or black),最后求最长的连续白色的区间的左右边界 瞬间想到上次求最大连续子串,线段树节点里用一个除了记录最大值,前驱最大值,后驱最大值以外,专门用个lm ,rm记录题目需要的坐标。这些都好处理,基本的线段树区间合并。要注意的是落实的叶子节点的时候,不能单纯以离散化之后的区间长度为衡量标准,要回到原区间去,原区间最长才是真正的最长。这个也好操作,读入数据的时候已经把原始区间保存下来了。 然后就是坑爹的离散化了,还是sha崽说得对,如果离散的两点距离大一1的话,必须要中间手动插点

线段树,区间更新

心不动则不痛 提交于 2020-01-12 17:00:52
把一个区间内的值全部修改为一个确定值; 计算某个区间内的最大值,最小值,和。 1 #include <cstdio> 2 #include <algorithm> 3 #include <cstdlib> 4 using namespace std; 5 #define INF 0x7f7f7f7f 6 #define Max 600000 7 long long a[Max], setv[Max], minv[Max], maxv[Max], sumv[Max], _max, _min, _sum, v; 8 int y1,y2; 9 bool flagv[Max]; 10 inline void maintain(int o, int L, int R) { 11 if (flagv[o]) minv[o] = maxv[o] = setv[o], sumv[o] = (R-L+1)*setv[o]; 12 else if (R > L) minv[o] = min(minv[o*2], minv[o*2+1]), maxv[o] = max(maxv[o*2], maxv[o*2+1]), 13 sumv[o] = sumv[o*2] + sumv[o*2+1]; 14 } 15 void build(int o, int L, int R) { 16 if (L == R)