线段树

机器学习——详解KD-Tree原理

混江龙づ霸主 提交于 2020-04-08 10:53:25
本文始发于个人公众号: TechFlow ,原创不易,求个关注 今天是机器学习的 第15篇文章 ,之前的文章当中讲了Kmeans的相关优化,还讲了大名鼎鼎的EM算法。有些小伙伴表示喜欢看这些硬核的,于是今天上点硬菜,我们来看一个机器学习领域经常用到的数据结构—— KD-Tree 。 从线段树到KD树 在讲KD树之前,我们先来了解一下 线段树 的概念。线段树在机器学习领域当中不太常见,作为高性能维护的数据结构,经常出现在各种算法比赛当中。线段树的本质是一棵维护一段区间的平衡二叉树。 比如下图就是一个经典的线段树: 从下图当中我们不难看出来,这棵线段树维护的是一个 区间内的最大值 。比如树根是8,维护的是整个区间的最大值,每一个中间节点的值都是以它为树根的子树中所有元素的最大值。 通过线段树,我们可以在 的时间内计算出某一个 连续区间的最大值 。比如我们来看下图: 当我们要求被框起来的区间中的最大值,我们只需要 找到能够覆盖这个区间的中间节点 就行。我们可以发现被红框框起来的两个节点的子树刚好覆盖这个区间,于是整个区间的最大值,就是这两个元素的最大值。这样,我们就把一个需要 查找的问题降低成了 ,不但如此,我们也可以 做到 复杂度内的更新 ,也就是说我们不但可以快速查询,还可以更新线段当中的元素。 当然线段树的应用非常广泛,也有 许多种变体 ,这里我们不过多深入

【luogu P1471】方差

丶灬走出姿态 提交于 2020-04-07 08:26:32
https://www.luogu.org/problem/show?pid=1471 一眼就能看出是线段树/树状数组题目了。 求平均不用说,线段树/树状数组维护区间和即可。 方差怎么求?先变换下方差公式: 可以看到区间的方差可以由区间内每个数的和与每个数的平方的和得来,用一棵线段树维护这两个东西就好了,好像写不了标记永久化。 当然写两棵普通的线段树/树状数组分别维护这两个东西或者分块暴力也可以 不过我写挂了 。 区间加的时候如何维护平方的和: 注意这里的 是指没有加之前的和。 #include <algorithm> #include <iostream> #define maxn 100005 using namespace std; namespace seg { struct node { int ln, rn, mn; long double sum[2], mark; } seg[maxn * 4]; void push_down(int p) { if (seg[p].mark && seg[p].ln != seg[p].rn) { seg[p * 2].mark += seg[p].mark; seg[p * 2].sum[1] += 2 * seg[p].mark * seg[p * 2].sum[0] + (seg[p * 2].rn - seg[p * 2]

【洛谷P3899】谈笑风生

ぃ、小莉子 提交于 2020-04-07 04:52:19
题目大意:给定一棵 N 个节点的有根树,1 号节点为根节点,现给出 Q 个询问,每次询问距离 u 号节点不超过 K 的节点 b,c 为 a 与 b 的后代,求这样的三元组有多少个。 题解:学会了线段树合并。 由于之前对线段树合并理解的不深刻,导致狂 WA 不止QAQ。 需要统计 \[\sum_{d e p t h(a)+k \geq d e p t h(b)} \operatorname({size}(b)-1) \] 这是一个二维数点问题,可以在一个维度上建立权值线段树,在另一个维度上统计权值和即可。 需要将询问离线处理,因为当 N 棵线段树合并完毕时,对于子节点的线段树来说,里面不仅有自己子树的权值,也有其他子树的权值(合并导致)。因此,需要在递归遍历每个节点时,在递归结束时进行对每个节点统计询问,最后统一输出即可。 时间复杂度和空间复杂度均为 \(O(nlogn)\) 。 代码如下 #include <bits/stdc++.h> #define pb push_back using namespace std; const int maxn=1e5+10; typedef long long ll; int n,a[maxn],d[maxn],cnt,ans[maxn]; vector<int> G[maxn]; struct node{ #define ls(o) t[o

可持久化线段树学习笔记

坚强是说给别人听的谎言 提交于 2020-04-04 12:46:10
最近学习了毒瘤的可持久化线段树,为了避免自己忘记,我决定还是做个笔记比较好,如果有什么问题欢迎大佬指出 可持久化是真的毒瘤,在网上找了很多资料才搞懂 (不过我觉得应该是我太蒻了) 首先以洛谷上的两个板子题为例吧 : P3834 【模板】可持久化线段树 1(主席树) P3919 【模板】可持久化数组(可持久化线段树/平衡树) 对于第一题,要求询问区间第K大(第K大指的是从小到大排序的第K个),直接扫是肯定不行的,因此我们需要可持久化线段树(感觉跳的好快,但是我也不清楚要怎么表达,就这样吧,反正知道是要用这个就行了) 首先既然是可持久化线段树,那么肯定需要用到线段树的,但是对于一般的线段树,每个节点维护的是对应区间的最值或者是和积,用这种线段树写可持久化我觉得是肯定不行的(emmm具体怎么样我也不清楚) 因此我们维护的是属于对应区间的元素的个数,建树代码如下,要注意开始需要排序后离散化: struct Tree{ int left; int right; int size; }node[5000001]; //用结构体存树节点的左孩子,右孩子和当前的权值 int tot_,a[5000001],b[5000001],N; int Build_ (int L,int R){ int now=++tot_; //now为当前节点编号 int mid=(L+R)>>1; //获取区间中点

[线段树模板题] 线段树2

好久不见. 提交于 2020-04-04 10:00:09
洛谷原题 第二次做 线段树2 了,之前研究了两节晚自习自以为完全理解了,但是今天写还是有一点小差错,值得反思。 稍微总结下线段树2的要点。 <1> 乘法标记影响加法标记 ,更新 乘法标记 时应将对应的 加法标记 乘上 乘法标记乘的值(key) <2> 标记下传中 乘法标记 应优先于 加法标记 ,由于我们在更新 乘法标记 时更新了 加法标记,所以如果乘法不优先的话,先乘再加的操作会出现误差 <3> 加或乘的操作中每一层算完儿子节点后都应更新当前节点信息,查询则不需要 ,更新操作只在 子区间 和 其子区间(down) 中进行,所以父区间都应由儿子更新 <4> memset有毒 ,害我找了40+分钟错误,我一开始用memset将乘法标记设为1,但样例怎么样都过不了,还是看了之前自己的题解,改成 建树过程中将标记设为1 才AC的 下面是我的代码(以前代码是真的丑) #include <bits/stdc++.h> using namespace std; long long a[100005],he[800005],lazy[800005],hard[800005]; int ll,rr,n,m; long long ans,P; void biu(int t,int l,int r) { hard[t]=1;//memset有毒,别用 if(l==r) { he[t]=a[l]%P;

线段树模板

独自空忆成欢 提交于 2020-04-04 09:58:08
1、求区间和 1 #include <cstdio> 2 #include <iostream> 3 using namespace std; 4 const int maxn=50000+5; 5 int a[maxn]; 6 struct Node 7 { 8 int l,r,sum; 9 } node[maxn<<2]; 10 11 //参数含义:节点区间,节点编号 12 void InitTree(int l,int r,int k) 13 { 14 node[k].r=r;//节点k的右子树 15 node[k].l=l; 16 node[k].sum=0;//权值 17 if(l==r)//到达叶子节点 18 { 19 node[k].sum=a[r]; 20 return; 21 } 22 int mid=(l+r)>>1; 23 InitTree(l,mid,k<<1);//递归建左子树 24 InitTree(mid+1,r,k<<1|1);//递归建右子树 25 26 node[k].sum=node[k<<1].sum+node[k<<1|1].sum; 27 //父节点的值为两子节点值的和 28 //此为线段树的核心所在,求最值修改这一句即可 29 } 30 void UpdateTree(int l,int r,int k,int sum) 31 { 32

线段树模板

限于喜欢 提交于 2020-04-04 09:57:38
自己写的模板,方便以后查看 求最大值的: #include<iostream> #include<cstdio> using namespace std; #define ls l,m,rt<<1 #define rs m+1,r,rt<<1|1 const int maxn=222222; int value[maxn<<2]; void PushUp(int rt) { value[rt]=max(value[rt<<1],value[rt<<1|1]); } void btree(int l,int r,int rt) { //初始为(1,n,1) if(l==r) { scanf("%d",&value[rt]); return ; } int m=(l+r)>>1; btree(ls); btree(rs); PushUp(rt); } void update(int x,int v,int l,int r,int rt) { //初始化为(a,b,1,n,1) if(l==r) { value[rt]=v; return ; } int m=(l+r)>>1; if(x<=m)update(x,v,ls); else update(x,v,rs); PushUp(rt); } int query(int L,int R,int l,int r,int rt) { /

hdu-1754 I Hate It---线段树模板题

南楼画角 提交于 2020-04-04 09:57:15
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1754 题目大意: 求区间最大值+单点修改 解题思路: 直接套用模板即可 1 #include<bits/stdc++.h> 2 #define MID(l, r) (l + (r - l) / 2) 3 #define lson(o) (o * 2) 4 #define rson(o) (o * 2 + 1) 5 using namespace std; 6 typedef long long ll; 7 const int INF = 1e9 +7; 8 const int maxn = 1e6 + 10; 9 int a[maxn]; 10 struct node 11 { 12 int l, r, mmax, mmin, sum; 13 }tree[maxn]; 14 void build(int o, int l, int r) 15 { 16 tree[o].l = l, tree[o].r = r; 17 if(l == r) 18 { 19 tree[o].mmax = tree[o].mmin = tree[o].sum = a[l]; 20 return; 21 } 22 int m = MID(l, r); 23 int lc = lson(o), rc =

POJ 3264 线段树模板题

末鹿安然 提交于 2020-04-04 09:56:18
之前做的那道是区间求和的,这道题是求区间最大值和最小值之差的,感觉这道题更简单。只需在插入时把每个区间的最大值最小值求出来保存在根节点上就可以啦~\(^o^)/ Balanced Lineup Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 39475 Accepted: 18524 Case Time Limit: 2000MS Description For the daily milking, Farmer John's N cows (1 ≤ N ≤ 50,000) always line up in the same order. One day Farmer John decides to organize a game of Ultimate Frisbee with some of the cows. To keep things simple, he will take a contiguous range of cows from the milking lineup to play the game. However, for all the cows to have fun they should not differ too much in height. Farmer John has

hdu 4747【线段树-成段更新】.cpp

爱⌒轻易说出口 提交于 2020-04-04 09:55:55
题意:   给出一个有n个数的数列,并定义mex(l, r)表示数列中第l个元素到第r个元素中第一个没有出现的最小非负整数。   求出这个数列中所有mex的值。    思路:   可以看出对于一个数列,mex(r, r~l)是一个递增序列   mex(0, 0~n-1)是很好求的,只需要遍历找出第一个没有出现的最小非负整数就好了。这里有一个小技巧: 1 tmp = 0; 2 for (int i = 1; i <= n; ++i) { 3 mp[arr[i]] = 1; 4 while (mp.find(tmp) != mp.end()) tmp++; 5 mex[i] = tmp; 6 } View Code   这样可以利用map中的红黑树很快找到第一个没有出现的最小非负整数。   然后在求mex(1~n-1, 0~n-1)的过程中,我们可以看出,每消除当前值arr[i],会影响到的是在下一个arr[i]出现前 往后的mex值中比arr[i]大的值,即如果当前这个值不存在了,那么在这个值下一次出现前,mex值比当前值大的mex值都应被替换成arr[i]。   所以我们可以再一次利用map的红黑树找到当前值下一次出现的位置,然后利用线段树成段更新往后的mex值和求出会影响到的mex值的个数。 1 for (int i = n; i >= 1; --i) { 2 if (mp