线段树

可持久化线段树

一个人想着一个人 提交于 2019-12-03 20:55:10
同步: https://buringstraw.win/archives/78/ 写了棵可持久化线段树,因为模拟赛里用到了主席树,而我却从来没写过。。。 凭借自己对当年上过的课的印象写的, 因为翻了很多博客没看懂 概述 每当有修改操作时,把需要修改的节点复制一份,在新复制的节点上完成修改操作。这里的修改也包括对点与点连接结构的修改。然后将每个版本的根节点存入 root 数组。 结构 这棵线段树需要动态开点,所以要有 struct struct node { int l, r;//对应的区间 int ch[2];//0:左儿子,1:右儿子 int v;//值 } c[MAXN]; 然后要有装初始元素的数组和 root 数组 int a[MAXN], root[MAXN]; 还要记录最新的节点和最新的版本 int newp, newv 操作 所有操作中都要注意:递归操作之后 newp 会改变 建树 跟普通线段树差不多 要先将根节点的 l , r 初始化好 root[++newv] = ++newp; scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i) { scanf("%d", a + i); } c[newp].l = 1; c[newp].r = n; void build (int p) { if (c[p].l == c[p]

线段树与树状数组

爷,独闯天下 提交于 2019-12-03 20:52:50
posted on 2019-10-06 22:08:50 【模板】线段树 1 #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #include <algorithm> using namespace std; typedef long long lld; int n ,m; const int N = 1e5 + 5; lld sum[N << 2], tag[N << 2]; void pushup(int u) { sum[u] = sum[u << 1] + sum[u << 1 | 1]; } void build(int u, int l, int r) { if(l == r) { cin >> sum[u]; return; } int mid = l + r >> 1; build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r); pushup(u); } void update(int u, int l, int r, int x) { tag[u] += x; sum[u] += (lld)x * (r - l + 1); } void pushdown(int u, int l

[模版]线段树入门

寵の児 提交于 2019-12-03 20:43:51
前言    线段树是OI中常用的数据结构,因为码量大, 比较毒瘤 ,导致我以前学的时候没怎么在意,这都马上退役了,我才来补坑   之前学的时候,网上的代码都不符合个人的审美,一直在纠结学哪一种好,如果你也有这样的烦恼,请读完这篇文章,说不定我的码风可能适合你 一、   作为数据结构,线段树可以在 Ο(log n) 的时间复杂度完成一次操作,建树的过程则需要 Ο(n)  模板题一   题目连接 : https://www.acwing.com/problem/content/description/244/   这是最基础的线段树,当然有人会说你有 lazy_tag 还最基础吗? 但是请想一想,如果没有 lazy_tag,线段树的存在是非还有意义呢   诚然,我已经相信你已经看过很多模板了,但是如果你觉得之前看过的代码不够美观,也可大致浏览一下   Code 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 typedef long long ll; 6 const int N=100002; 7 inline ll read() 8 { 9 char ch=getchar(); 10 ll x=0;bool f=false; 11 while (

xor or and 线段树

谁说胖子不能爱 提交于 2019-12-03 20:26:59
每一位维护一颗线段树 (-1)^1 =-2 (-2)^1=-1 #include <cstdio> #include<iostream> using namespace std; //#define int long long #define si signed #define sc(x) scanf("%d", &x); #define P pair<int, int> int lazy[4][4000005]; int sum[4][4000005]; int A[1000005]; int n, m; void pushdown(int id, int x, int l, int r) { if (lazy[id][x] == 1) { lazy[id][x] = 0; int mid = l + r >> 1; lazy[id][x << 1] ^= 1; sum[id][x << 1] = (mid - l + 1) - sum[id][x << 1]; lazy[id][x << 1 | 1] ^= 1; sum[id][x << 1 | 1] = (r - mid) - sum[id][x << 1 | 1]; } else if (lazy[id][x] == -2) { lazy[id][x] = 0; int mid = l + r >> 1; lazy[id]

6411. 【NOIP2019模拟11.06】上网

元气小坏坏 提交于 2019-12-03 20:19:55
题目描述 Description Input Output 若无解,则输出”Impossible”。 否则第一行输出”Possible”,第二行输出 n 个正整数,依次输出序列 a 中每个数。 Sample Input 5 2 2 2 7 5 3 1 4 2 2 3 4 5 1 4 Sample Output Possible 6 7 1000000000 6 3 Data Constraint 题解 线段树优化连边 ki向xij连边,xi与xi+1间的点向ki连边(线段树),线段树的点从下往上连边 一条从u到v的权值为s的边的意义是f[u]+s<=f[v],拓扑求max即可 初值就是给出的p和d(不需要求出相对大小然后再搞) code #include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #define fo(a,b,c) for (a=b; a<=c; a++) #define fd(a,b,c) for (a=b; a>=c; a--) #define min(a,b) (a<b?a:b) #define max(a,b) (a>b?a:b) using namespace std; struct type{ int s,x; } b

LG3834 可持久化线段树1

我们两清 提交于 2019-12-03 17:36:25
题意 给定 \(N\) 个整数构成的序列,将对于指定的闭区间查询其区间内的第 \(K\) 小值。 $n \leq 2 \times 10^5 $ 思路 在 \([l,r]\) 区间内的数的个数,可以用 \(sum[r]-sum[l]\) 来计算,这样的话就很容易想到要开n棵权值线段树,但是一看范围,很显然会 \(mle\) ,于是就有一个叫主席树的东西出现了。 当新插入一个数的时候,会发现,只有一条路径上的 \(sum\) 会发生变化,其实只要复制这一条路径上的结点就好了。 插入时,如果它对右儿子无影响,那么将它的右儿子指向原先的树,左儿子继续进行插入操作,反之亦然。查找时用到差分进行左右路径的选择,一直走下去就好了。 #include <bits/stdc++.h> using namespace std; const int N=200005; int n,m,a[N],b[N],s1[N*40],s2[N*40],sum[N*40],T[N],l,r,x,tot,n2; int update(int pre,int l,int r,int x){ int rt=++tot; s1[rt]=s1[pre],s2[rt]=s2[pre],sum[rt]=sum[pre]+1; if (l>=r) return rt; int mid=(l+r)>>1; if (x<=mid)

P5025 [SNOI2017]炸弹

我只是一个虾纸丫 提交于 2019-12-03 17:09:54
原题链接 https://www.luogu.org/problem/P5025 闲话时刻: 第一道 AC 的黑题,虽然众人皆说水。。。 其实思路不是很难,代码也不是很难打,是一些我们已经学过的东西凝合在一起,只要基础扎实的话,做出这道题目来说也就很简单了(不包括我); 题目大意: 有 n 个点,每个点可影响到它左右各 R [ i ] 范围内的点,并且影响到的点会产生连锁反应,求每个点能影响到多少个点; 题解: 一个很简单的思路: 向每个炸弹爆炸范围内的其他炸弹连一条 有向边 < u , v >,表示 u 能炸到 v,最后我们从每个点开始跑 dfs,看看能到达多少个点就好了; 但是。。。 这样连边的话,最劣情况下会连 n 2 条边,看一眼 n 的范围: N ≤ 5 0 0 0 0 0,嗯,显然不行 ~o(* ̄▽ ̄*)o; 考虑建边优化: 不难想到一个炸弹的爆炸范围是一个长度为 2 * R [ i ] 的区间,这个区间内的所有炸弹都会被引爆,因此被引爆的炸弹也是一个连续的区间; 区间操作?你想到了什么? 线段树优化建边 假如说一个炸弹 x 能炸到第 2~6 个炸弹,考虑怎么建边: 一般操作: 线段树优化建边: 建了 5 条边?看我的! 我们发现,这种建边方式只需要建 ⌈ log 2 5 ⌉ = 3 条边; 看了上面的图,应该对 线段树优化建边 有了一个初步的认识了:

CF786B Legacy 线段树优化建图

半城伤御伤魂 提交于 2019-12-03 15:08:20
问题描述 CF786B LG-CF786B 题解 线段树优化建图 线段树的一个区间结点代表 \([l,r]\) 区间点。 然后建立区间点的时候就在线段树上建边,有效减少点的个数,从而提高时空效率。 优质题解传送门 \(\mathrm{Code}\) #include<bits/stdc++.h> using namespace std; #define int long long template <typename Tp> void read(Tp &x){ x=0;char ch=1;int fh; while(ch!='-'&&(ch>'9'||ch<'0')) ch=getchar(); if(ch=='-') ch=getchar(),fh=-1; else fh=1; while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); x*=fh; } const int maxn=100100; int INF=0x3f3f3f3f3f3f3f3fll; #define pii(x,y) make_pair(x,y) #define mid ((l+r)>>1) #define lfc (x<<1) #define rgc ((x<<1)|1) vector < pair < int , int > > e

Luogu P5490 【模板】扫描线

风格不统一 提交于 2019-12-03 15:00:14
传送门 作为给dtx的妹子讲题的交换他给我讲的 果然线段树最可爱了w 扫描线可以用来求矩形的面积并… 一个平面上有一些有重叠的矩形,求他们的并集的面积。 直接放网上的图了x 对于每个矩形,将y坐标拆成两个修改操作(插入和删除),从下到上排序; 将x坐标unique离散化,从左到右排序,用线段树维护切割出来的每条线段。 假设有一根从x轴出发,向上移动的线——也就是扫描线! 遇到修改时,就在线段树上对应区间加上或减掉线段,答案加上(当前线段长度之和)*(这个修改到下个修改的y坐标之差)。 线段树中维护什么信息? sum[i]表示区间i的线段总长度。显然,sum[i] = sum[ls]+sum[rs]。 但是对于插入和删除,不能直接修改sum的值,因为一个线段可能被多个矩形覆盖...删除了一个之后还可能有另一个。 lazy[i]表示区间i被几个矩形覆盖。这样,修改的时候直接在lazy上++--就好了。 而且一定是先加再减,所以lazy标记一定不会为负数。 可以发现,每次的查询操作都是查询整个区间。 那么只需要考虑上传,不用下传。 若lazy[i]>0,即被覆盖,则sum[i] = 区间i的长度; 否则,sum[i] = sum[ls]+sum[rs]。 注意: 在传统的——维护点的线段树里,(1,4) = (1,2)+(3,4); 但维护线段时,线段(2,3)被忽略掉了。

【模板】【线段树】矩形面积交 矩形周长

拟墨画扇 提交于 2019-12-03 14:39:29
【1】底边固定的矩形面积交 这是我第一次不知道知识点的时候,瞎用区间最大最小值,做的面积交 还挺快 #include<cstdio> #include<cstdlib> #include<algorithm> #define lnt long long using namespace std; int n; const int N=1e9+3,M=40003; struct node { int x,id,pos; bool operator < (const node & o ) const { return x<o.x; } }ask[M<<1]; int tot,ll[M][2],h[M]; int td[M][2],d[M<<1]; int root,cnt; int ls[M<<2],rs[M<<2],len[M<<2]; int laz[M<<2],mn[M<<2],mx[M<<2]; void updata(int rt) { mn[rt]=min(mn[ls[rt]],mn[rs[rt]]), mx[rt]=max(mx[ls[rt]],mx[rs[rt]]); } void build(int &rt,int l,int r) { rt=++cnt; if(l==r) { len[rt]=d[l+1]-d[l]; return ; } int mid=(l+r)>