线段树

天神下凡

断了今生、忘了曾经 提交于 2020-03-17 06:16:15
https://zybuluo.com/ysner/note/1176892 题面 给出 \(n\) 个圆心同在 \(x\) 轴上的圆,问圆的轮廓将平面分为几块?( 保证圆互不相交 ) \(n\leq3*10^5\) \(r\leq10^9,x\leq10^9\) 解析 经过观察,可以发现,如果一个圆的直径被其它小圆的直径完全覆盖,就会产生额外一贡献。 因为圆在一条直线上,我们只用关心其在直线上的一部分,即可以 用直径代替一个圆 。 一条直线再次被覆盖?能想到什么? 线段树?栈? \(method\ 1\) 并查集 据说这是我的原创方法? 在手玩样例时,我发现, 每次产生贡献的前一步都是把已联通(通过圆相切的方式)的两个点作为一个圆直径的两端点 。 如何判断两点是否联通? 每覆盖一条直线,两端点就连通了,此时两端点中间的点就没用了,直接把两端点纳入同一并查集即可。 当然,两端点联通有一种方式叫 在同一圆直径的两端 ,即存在同圆,这会影响答案,要排序除掉。 由于半径范围(即直径两端点范围)过大,要用 \(map\) 。 于是复杂度达到了 \(O(nlog^2n)\) ,但时间比为 \(1.16s/3s\) ,不虚。(并查集+ \(map\) ) 可以通过离散化达到 \(O(nlogn)\) #include<iostream> #include<cmath> #include

洛谷P4065 [JXOI2017]颜色(线段树)

无人久伴 提交于 2020-03-15 02:08:45
题意 题目链接 Sol 线段树板子题都做不出来,真是越来越菜了。。 根据题目描述,一个合法区间等价于在区间内的颜色没有在区间外出现过。 所以我们可以对于每个右端点,统计最长的左端点在哪里,刚开始以为这个东西有单调性,但事实并不是这样。。 我们统计出对于每个颜色最优的位置 \(r_i\) 和最左的位置 \(l_i\) 那么对于某个左端点 \(j\) ,如果 \(r_j > i\) ,那么 \(j\) 以及它左侧的点都是不能选的,这里可以用堆+multiset维护。 若 \(r_j \leqslant i\) ,那么 \((l_j, r_j]\) 都是不能选的。 然后直接线段树区间赋值就好了 // luogu-judger-enable-o2 #include<bits/stdc++.h> #define Fin(x) freopen(#x".in", "r", stdin); #define Pair pair<int, int> #define fi first #define se second template<typename A, typename B> inline bool chmax(A &x, B y) {return x < y ? x = y, 1 : 0;} template<typename A, typename B> inline bool chmin

HYSBZ 4034 dfs序线段树

元气小坏坏 提交于 2020-03-13 12:31:12
题意 反向 翻译: A tree with N points, with point 1 as the root, and the tree points have edge weights. Then there are M There are three types of operations: Operation 1: Increase a member's point weight by a. Operation 2: Increase the point weights of all points in a subtree with x as the root by a. Operation 3: Ask the sum of the weights of all points in the path from a shareholder x to the root. The first line contains two integers N, M. Represents points and operands. The next line of N integers represents the initial weights of the nodes in the tree. Next N-1 Each row contains three positive

Billboard HDU - 2795 线段树应用

强颜欢笑 提交于 2020-03-12 06:52:28
  题意:有一块高为h宽为w的广告牌,有n张高为1宽为wi的小广告,每次都可以在广告牌上贴广告,贴广告尽量往上贴,相同高度尽量往左贴,每次贴广告之后求该广告所在的行数,没有合适的位置就输出-1。   思路:广告牌高度维护一棵线段树,每个节点维护区间左端点l,区间右端点r,区间最大值Max,Max的值表示该区间最多空位哪一行的空位长度,与小广告的的长度进行对比,但是要优先查找左子区间(因为需要尽量往高处贴),左子区间的Max小于输入长度,再查找右子区间,如果右子区间的Max也小于输入长度,说明没有合适的位置,返回-1。   这题h,w的数据范围时1e9,但是n的范围却是200000,考虑到广告的高度是1,所以h最大其实只有200000。 # include <iostream> # include <algorithm> # include <cstdio> # include <cstring> using namespace std ; const int MAX_N = 200010 ; int h , w , n ; struct node { int l , r , Max ; } tr [ MAX_N * 4 ] ; int x ; void push_up ( int p ) { tr [ p ] . Max = max ( tr [ p << 1 ] . Max ,

线段树——线段树线段扫描(板子)

狂风中的少年 提交于 2020-03-12 02:55:31
线段树——线段树线段扫描 # include <iostream> # include <algorithm> using namespace std ; const int maxn = 1e5 ; struct Line { double l , r , h ; int d ; bool operator < ( const Line & a ) const { return h < a . h ; } } line [ maxn ] ; double X [ maxn ] ; //用于离散化的 int mark [ maxn << 2 ] ; //用与统计 下行边 与 上行边 之差 double sum [ maxn << 2 ] ; //用于统计当前某个区间段的长度 int Search ( double x , double X [ ] , int Len ) { int l = 0 , r = Len - 1 ; while ( l <= r ) { int m = ( l + r ) >> 1 ; if ( X [ m ] == x ) return m ; else if ( X [ m ] > x ) r = m - 1 ; else l = m + 1 ; } return - 1 ; } void Push_up ( int pos , int L , int

效率至上(线段树)

白昼怎懂夜的黑 提交于 2020-03-12 02:03:56
# include <bits/stdc++.h> using namespace std ; # define INF INT_MAX struct node { int maxx , minn ; int left , right ; } ; node tree [ 1000001 ] ; int num [ 50005 ] ; void build ( int id , int start , int end ) { tree [ id ] . left = start ; tree [ id ] . right = end ; if ( start == end ) { tree [ id ] . maxx = num [ start ] ; tree [ id ] . minn = num [ start ] ; } else { int mid = ( start + end ) / 2 ; build ( id * 2 + 1 , start , mid ) ; build ( id * 2 + 2 , mid + 1 , end ) ; tree [ id ] . maxx = max ( tree [ 2 * id + 1 ] . maxx , tree [ 2 * id + 2 ] . maxx ) ; tree [ id ] . minn = min (

POJ 3667——Hotel(线段树)

与世无争的帅哥 提交于 2020-03-11 09:21:35
题目链接: http://poj.org/problem?id=3667 题目大意:给n个房间,m次操作,2种操作,1是找到最左边的连续d个空房间入住返回开头房间的位置,2从x开始的d个房间变成空房间。 代码 #include <iostream> #include <string> #include <cstdio> #include <algorithm> #include <cstring> #include <cmath> #include <queue> #include <functional> #include <map> #include <set> #include <stack> #define FT(a, b) memset(a, b, sizeof(a)) #define FAT(a) memset(a, 0, sizeof(a)) using namespace std; typedef long long ll; const int M = 5e4 + 10; const int INF = 0x3f3f3f3f; struct node { int l, r, msum, lsum, rsum, lazy; } tree[M * 4]; int n, m; void push_up(int root) { node &l = tree[root <<

洛谷P3605 线段树合并

老子叫甜甜 提交于 2020-03-10 00:31:27
题目链接:https://www.luogu.com.cn/problem/P3605 题目大意:给出一颗树,每个点都有一个权值,最后对于每个点,输出在它的子树中,有多少个点的权值比它大。 把权值取负,就是p[j]<p[i]的节点个数。直接线段树合并。 # include <bits/stdc++.h> # define LL long long using namespace std ; const int N = 1e5 + 5 ; int w [ N ] , p [ N ] , lc [ N * 20 ] , rc [ N * 20 ] , rt [ N * 20 ] , tot = 0 , n ; vector < int > v [ N ] ; int tre [ N * 20 ] , ans [ N ] ; //节点信息 void Insert ( int & i , int l , int r , int x ) { //建树 if ( r < l ) return ; i = ++ tot ; //动态开点 if ( l == r ) { tre [ i ] ++ ; return ; } int mid = ( l + r ) >> 1 ; if ( x <= mid ) Insert ( lc [ i ] , l , mid , x ) ; if ( x >

BZOJ.4552.[HEOI2016/TJOI2016]排序(线段树合并/二分 线段树)

与世无争的帅哥 提交于 2020-03-09 20:08:47
题目链接 对于序列上每一段连续区间的数我们都可以动态开点建一棵值域线段树。初始时就是 \(n\) 棵。 对于每次操作,我们可以将 \([l,r]\) 的数分别从之前它所属的若干段区间中分离出来,合并。 对于升序与降序,只需要维护一个标记,若为降序,则给左区间大的那部分。 具体实现还要用set存下每棵线段树维护的区间左端点,便于快速查找包含 \([l,r]\) 的区间;对每个区间维护其右端点便于快速得到区间大小。 时间、空间复杂度都是 \(O((n+m)\log n)\) 。 但是在洛谷上要么RE要么MLE。。其它OJ上还是能过的。 Another Solution: 对于询问二分一个值,将所有数根据与这个值的大小关系设为0/1。模拟每次操作,就是将一段区间的0/1分别放在两边。用线段树维护区间和、区间覆盖即可。最后判断是否仅p之前全是0, 线段树合并做法: //57628kb 1692ms #include <set> #include <cstdio> #include <cctype> #include <algorithm> //#define gc() getchar() #define MAXIN 150000 #define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)

线段树合并

偶尔善良 提交于 2020-03-09 20:07:55
  线段树合并就是把两个维护相同区间的线段树合并到一块.   什么叫维护相同区间?就是每次操作的最大的那个区间是一样的,根节点维护的区间一样,根节点的左儿子维护的也一样,右儿子维护的也一样,左儿子的左儿子....   显然只需要建树的时候都建成一样的就好了.一般我们采用动态开点.   由于线段树合并的均摊复杂度是log的, 虽然我看不懂证明 可以开成权值线段树.   对于两个线段树该如何合并呢?我们递归的定义:   1.对于线段树a,b.如果只有a有当前区间或只有b有当前区间,那么把那个认定为新线段树的节点.   2.否则我们同时递归a的左儿子和b的左儿子,递归完后把刚认定的节点作为a的左儿子.   3.递归a的右儿子和b的右儿子,递归完后把刚认定的节点作为a的右儿子.   4.把a认定为新线段树的节点.   如果有两个满的线段树,每次复杂度是O(n)还多的.但是如果刚开始有n个线段树,每个线段树用动态开点维护一个数,那么合并n-1次后均摊复杂度是nlogn的.   校内oj并没有找到例题.我口述一个例题好了.   给你一棵有n个点的树,树上每个节点都有一种颜色 ci,求以每个点为根的子树出现最多的颜色的和.   这是一个最简单的线段树合并了.先对每个节点的每个颜色做一个权值线段树,从根dfs整颗树.dfs完儿子后,合并父亲和儿子的权值线段树,记录答案.没了.   口胡还是开心的