线段树

线段树(区间查询,区间修改)——标记永久化版

烂漫一生 提交于 2019-12-01 19:59:06
传送门: https://www.luogu.org/problem/P3372 为了不下传add的标记,改为在询问的过程当中计算每个遇到的节点对当前询问的影响。而为了保证询问的复杂度,子节点的影响需要在修改操作时计算好。因此实际上,add的值表示这个区间共同加上的值,seg表示这个区间内除了add之外其它数的值的和。需要注意,区间的add值可能有一部分在祖先节点上,这在递归时累加即可。 1 #include<bits/stdc++.h> 2 using namespace std; 3 long long n,m; 4 long long num[500009]; 5 long long seg[500009];//线段树上除了add之外的值 6 long long add[500009];//当前区间共同加上的值 7 inline long long read() 8 { 9 long long x=0,f=1;char ch=getchar(); 10 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 11 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} 12 return x*f; 13 } 14 inline long long ls

「CF319E」Ping-Pong「线段树」「并查集」

梦想与她 提交于 2019-12-01 19:48:30
题意 规定区间 \((a,b)\) 到区间 \((c,d)\) 有边当且仅当 \(c<a<d\) 或 \(c<b<d\) 。 起初区间集合为空。有 \(n\) ( \(n\leq 10^5\) )次操作,每次操作形如: \(1\) \(x\) \(y\) ( \()|x|,|y|\leq10^9)\) :加入一个新区间 \((x,y)\) ,保证新区间长度最长 \(2\) \(x\) \(y\) :询问第 \(i\) 个加入第区间能否到达第 \(j\) 个加入第区间,保证询问合法 题解 考虑连边的两种情况:第一种是包含,小的向大的连边;第二种是相交不包含,连双向边。 注意到答案最后不会走超过 \(1\) 条单向边,我们先考虑双向边。 由于是双向边,我们可以用并查集维护连通块。那对于一个新区间,我们得考虑它和哪些区间连双向边。 可以使用线段树来做这件事。把区间拆放到 \(\log\) 个线段树结点上。加新区间连边就把左右端点对应线段树叶子结点到根到路径上所有放的路径都合并掉,然后把标记改成自己。题目给了一个每次加入一个最长区间的限制,这样就保证了新区间不会被包含。复杂度也是对的,每个区间分 \(\log\) 段,每段加入一次删除一次。 询问就判是同个并查集,或者是包含关系就YES,否则NO。注意一个连通块要维护L和R用于判包含。 时间复杂度 \(O(n\alpha(n)\log n)

线段树-维护区间合并

二次信任 提交于 2019-12-01 19:20:53
题意: http://acm.hdu.edu.cn/showproblem.php?pid=1540 写了我老久 1 #define IOS ios_base::sync_with_stdio(0); cin.tie(0); 2 #include <cstdio>//sprintf islower isupper 3 #include <cstdlib>//malloc exit strcat itoa system("cls") 4 #include <iostream>//pair 5 #include <fstream>//freopen("C:\\Users\\13606\\Desktop\\草稿.txt","r",stdin); 6 #include <bitset> 7 //#include <map> 8 //#include<unordered_map> 9 #include <vector> 10 #include <stack> 11 #include <set> 12 #include <string.h>//strstr substr 13 #include <string> 14 #include <time.h>//srand(((unsigned)time(NULL))); Seed n=rand()%10 - 0~9; 15 #include

线段树合并学习笔记

喜你入骨 提交于 2019-12-01 13:42:46
线段树合并学习笔记 学了一波,其实类似于fhq treap, 直接贴代码吧: void merge(int &x,int y,int l,int r){ if(!x||!y){x=x+y; return;} int mid=l+r>>1; w[x]+=w[y]; merge(lc,son[y][0],l,mid),merge(rc,son[y][1],mid+1,r); } 至于为什么总复杂度是 \(O(n log n)\) : 在某一机房大佬为我讲解后总算明白了?: 其实线段树合并是O(总结点数)的,但一般开始时每一刻线段树(动态开点)有 \(log n\) 个节点,便是 \(O(n log n)\) 的了, 为什么呢? 每次合并, 我们就继承某一线段树没有、另一线段树有的节点, 合并消除共有的节点,(每一次递归消除一个), 所以消除总结点数个,需要同样的递归次数了。 完结撒花。 ____ / / |>_< | / / -- -- 来源: https://www.cnblogs.com/ljk123-de-bo-ke/p/11688871.html

SCUT - 85 - 香料 = 线段树

≯℡__Kan透↙ 提交于 2019-12-01 13:36:59
https://scut.online/p/85 一个区间修改线段树的模板题,不过挺有意思的,每个格子有一个单位价格,要把一段区间的总价格算出来,每次update一个区间的重量。 发现总价格实际上是满足结合律的。直接上线段树。 #include<bits/stdc++.h> using namespace std; typedef long long ll; #define lt ls, l, m #define rt rs, m + 1, r #define ls (o<<1) #define rs (o<<1|1) const ll mod = 1008610010; const int MAXM = 100000 + 5; ll a[MAXM]; ll st[MAXM * 4], p[MAXM * 4], lazy[MAXM * 4]; inline void PushUp(int o) { st[o] = (st[ls] + st[rs]) % mod; } inline void PushDown(int o, int l, int r) { if(lazy[o]) { lazy[ls] = (lazy[ls] + lazy[o]) % mod; lazy[rs] = (lazy[rs] + lazy[o]) % mod; int m = l + r >> 1; st

HDU - 3308 LCIS (线段树区间合并)

走远了吗. 提交于 2019-12-01 13:31:14
题意: 给定n个整数。 你有两种操作: U A B:用B替换第A个数。(编号从0开始) Q A B:输出[a,b]中最长连续严格递增子序列(LCIS)的长度。 第一行有一个整数T,表示数据组数。 每组数据以两个整数n,m(0 <n,m <= 100000)开始。 下一行有n个整数(0 <= val <= 100000)。 接下来的m行每行表示一个操作: U A B(0 <= A,n,0 <= B = 100000) 或 Q A B(0 <= A <= B <n)。 对于每个询问,输出答案。 思路: 结点修改以及区间查询,我们很容易想到线段树去实现。同时要求 LCIS 我们会想到线段树存储区间最大值的操作。但是关键在于区间如何去更新。 当左右两个子树区间向上合并的时候,父亲区间的最大值应该怎么更新。 进行分类讨论: (1)合并时中间部分不会连接:左右区间合并时由于 左子树的右端值大于等于右子树的左端值,所以中间部分不能连接 (这样该区间最大值就等于 左右子树区间的最大值) (2)如果要进行连接:{我们就需要记录某个区间的左右端连续长度,以及左右端点的值} (i)判断是否更新 合并区间左右端连续长度,由于中间连接,所以如果左子树左端连续长度等于其区间长度,(即整个区间连续)则需要更新 合并区间左端长度 = 左子树左端连续长度+右子树左端连续长度 同理,合并区间右端长度 =

线段树

ⅰ亾dé卋堺 提交于 2019-12-01 13:26:54
例题: https://www.luogu.org/problem/P3372 (洛谷) 线段树之单点更新: 模板: #include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1E5+7; ll arr[N]; ll tree[N+N+N]; //如果我们的tree从0开始,那么左儿子为2*node+1,右儿子为2*node+2; //从1开始的话,左二子就是2*node,右儿子是2*node+1; //在此我们都从0开始; void buid_tree(ll node,ll start,ll end){//建树 if(start==end){ tree[node]=arr[end]; return ; } ll mid=(start+end)>>1; ll left_node =2*node+1; ll right_node =2*node+2; buid_tree(left_node ,start,mid); buid_tree(right_node,mid+1,end); tree[node]=tree[left_node]+tree[right_node]; } void update_tree(ll node,ll start,ll end,ll idx,ll value)

(模板)线段树

北战南征 提交于 2019-12-01 13:23:55
—————————————————————————————————————————————————— —————————————————————前排护眼——————————————————————— —————————————————————————————————————————————————— 单点更新+区间查询 typedef long long ll; ll n,m; ll value[1000002]; ll data[200002]; void build(int o,int l,int r){ //建树 if(l==r){ value[o]=value[l]; return ; } ll mid=(l+r)/2; build(o*2,l,m); build(o*2+1,m+1,r); value[o]=max(value[o*2],value[o*2+1]); } void update(int o,int l,int r,int x,int d){ //单点更新,将x的value改为d if(l==r){ value[o]=d; return ; } int m=(l+r)/2; if(x<=m)update(o*2,l,m,x,d); if(m+1<=x)update(o*2+1,m+1,r,x,d); value[o]=max(value[o*2],value

CF786B Legacy 线段树优化建图

北城余情 提交于 2019-12-01 13:16:01
CF786B Legacy ### 线段树优化建图 Luogu链接 裸题,区间连点,点连区间 假如直接连边跑的话一定会T 这时候就需要线段树优化建图了 两个线段树 一个树是区间连点的,叫out 一个树是点连区间的,叫in 但是两个树内部连边的方向不一样 如图 假如相反必然就不对了包含关系错了 剩下的就好写了 代码如下: #include<bits/stdc++.h> #define mk make_pair #define int long long using namespace std; const int maxn=1e5+10,inf=0x3f3f3f3f3f3f3f3f; int n,q,st,cnt,trin[maxn<<2],trot[maxn<<2]; vector<pair<int,int> > G[maxn*10]; void build(int p,int l,int r){ if(l==r){ trin[p]=l;trot[p]=r;return; } int mid=(l+r)>>1; build(p<<1,l,mid);build(p<<1|1,mid+1,r); trin[p]=++cnt;trot[p]=++cnt; G[trot[p<<1]].push_back(mk(trot[p],0)); G[trot[p<<1|1]].push_back

线段树入门(汇总)

折月煮酒 提交于 2019-12-01 13:03:12
标签:线段树 这篇文章主要内容主要来自 NotOnlySuccess 大神若干年前博客中的博文 《【完全版】线段树》,由于时间有些久远,现在已经找不到大神的原博文了,所以整理了一些网上的资料,在这里还原一下。 代码风格 maxn 是题目给的最大区间,而节点数要开4倍,确切来说节点数要开大于 maxn 的最小 \(2_x\) 的两倍; lson 和 rson 分别表示节点的左儿子和右儿子,由于每次传参数的时候都固定是这几个变量,所以可以用预定义比较方便地表示; push_up(int rt) 是把当前节点的信息更新到父节点; push_down(int rt) 是把当前节点的信息更新给儿子节点; rt 表示当前子树的根(root),也就是当前所在的节点。 题型分类 单点更新 :最最基础的线段树,只更新叶子节点,然后把信息用 push_up(int rt) 这个函数更新上来; 成段更新 :(通常这对初学者来说是一道坎)需要用到延迟标记(或者说懒惰标记),简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新 or 询问到的时候; 区间合并 :这类题目会询问区间中满足条件的连续最长区间,所以 push_up 的手需要对左右儿子的区间进行合并; 扫描线 :这类题目需要将一些操作排序,然后从左到右用一根扫描线(当然是我们脑子里)扫过去,最简单的就是矩形面积并