Aleph1022
2018-08-03 13:56:12
线段树这种玩意儿,是一种比较基础的数据结构,若是没有理解或是不了解的的请出门左转往期日报:#4 [皎月半洒花] 浅谈线段树(Segment Tree)
有勇气继续看的泥萌都掌握了吧
有言在先:我的码风可能有点迥异,请见谅……
权值线段树和线段树,唯一的本质的区别就是他们维护的东西不一样,因此权值线段树可以查询全局第
权值线段树一般支持以下三个操作:
insert
erase/remove
query
insert
操作,其实主要思路就是递归找到新加入的权值对应的结点,然后进行更新该节点的信息。
void insert(int k,int root = 1)
{
if(seg[root].l == seg[root].r)
{
//Update the information
return ;
}
int mid = seg[root].l + seg[root].r >> 1;
if(k <= mid)//在左儿子当中
insert(k,lson);
if(k > mid)//在右儿子当中
insert(k,rson);
//Push up the information
}
remove
操作,其实也就是把权值对应的结点的信息恢复到insert
之前,代码与insert
异曲同工。
void remove(int k,int root = 1)
{
if(seg[root].l == seg[root].r)
{
//Remove the information
return ;
}
int mid = seg[root].l + seg[root].r >> 1;
if(k <= mid)//在左儿子当中
remove(k,lson);
if(k > mid)//在右儿子当中
remove(k,rson);
//Push up the information
}
其实并没有什么区别(雾)
query
操作,对于不同的询问有不同的写法,主要思路和线段树差不多。有时候只是查询整个数列的答案的话就可以直接输出根节点维护的信息。
经典操作:查询k
小值。
我们可以让每个结点维护自己区间内出现了多少个数,然后从根节点开始找,每次判断
然后以此类推,继续递归找下去。
代码:
int query(int k,int root = 1)
{
if(seg[root].l == seg[root].r)
return seg[root].l;//找到了
int mid = seg[lson].tot;//左儿子区间的数字个数
if(k <= mid)
return query(k,lson);//左儿子里面找
else if(k > mid)
return query(k - mid,rson);//右儿子里面找
}
这个查询的代码要好好理解,下面不会再讲。
此外,权值线段树再加上可持久化就是我们hjt
大佬的主席树,就可以通过历史版本查询区间第
那么,我们引入一道例题:
最近小
Add x
如果当前的手牌中没有权值为x
的塔罗牌则加入,否则忽略该操作。
Remove x
如果当前的手牌中有权值为x
的塔罗牌则弃掉该牌,否则忽略该操作。
Query
查询当前手牌中权值最接近的两张牌的权值之差,如果当前手牌数量少于
第一行,一个整数
接下来
对于每个Query
操作,输出一行,表示操作的结果。
12
Add 1
Remove 2
Add 1
Query
Add 7
Query
Add 10
Query
Add 5
Query
Remove 7
Query
-1
6
3
2
4
改编自学校OJ
一题
注意,对于各个操作的描述,可以看出塔罗牌的权值不能重复!
那么,就可以构造一棵根节点区间为在树上乱搞,这就是权值线段树的核心思想——(划重点)以数据范围为区间进行答案的维护(这句话是自己说的说错了别打我(逃)
大概是这样的:
对于每个结点,我们维护三个值:
递归维护的时候,
这个不难理解,但是最小差呢……
思考一下,容易发现更新的时候有三种状态:
左儿子
右儿子
右儿子区间维护的最小值与左儿子区间维护的最大值的差
然后对于三种情况,
一个技巧:建树的时候可以把最小值初始化为正无穷,把最大值初始化为负无穷,这样更新最小差的时候只需要对第三种状态判断左右最值是否为无穷即可,因为对无穷作
参考代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#define lson (root << 1)
#define rson (lson | 1)
using namespace std;
int n,x;
char opt[15];
struct node
{
int l,r;
int max,min,diff;
} seg[400010];
void build(int l,int r,int root = 1)//初始建树操作
{
seg[root].min = seg[root].diff = 0x3f3f3f3f;
seg[root].max = -0x3f3f3f3f;
seg[root].l = l,seg[root].r = r;
//以上初值不多说
if(l == r)
return ;
int mid = l + r >> 1;
build(l,mid,lson);
build(mid + 1,r,rson);
}
void generate(int x,int root = 1)//加牌操作
{
if(seg[root].l == seg[root].r)
{
seg[root].max = seg[root].min = x;
return ;
}
int mid = seg[root].l + seg[root].r >> 1;
if(x <= mid)
generate(x,lson);
if(x > mid)
generate(x,rson);
seg[root].max = max(seg[lson].max,seg[rson].max);
seg[root].min = min(seg[lson].min,seg[rson].min);
if(seg[lson].max == -0x3f3f3f3f || seg[rson].min == 0x3f3f3f3f)
seg[root].diff = min(seg[lson].diff,seg[rson].diff);
else
seg[root].diff = min(seg[rson].min - seg[lson].max,min(seg[lson].diff,seg[rson].diff));
}
void remove(int x,int root = 1)//弃牌操作
{
if(seg[root].l == seg[root].r)
{
seg[root].max = -0x3f3f3f3f;
seg[root].min = 0x3f3f3f3f;
return ;
}
int mid = seg[root].l + seg[root].r >> 1;
if(x <= mid)
remove(x,lson);
if(x > mid)
remove(x,rson);
seg[root].max = max(seg[lson].max,seg[rson].max);
seg[root].min = min(seg[lson].min,seg[rson].min);
if(seg[lson].max == -0x3f3f3f3f || seg[rson].min == 0x3f3f3f3f)
seg[root].diff = min(seg[lson].diff,seg[rson].diff);
else
seg[root].diff = min(seg[rson].min - seg[lson].max,min(seg[lson].diff,seg[rson].diff));
}
int main()
{
scanf("%d",&n);
build(1,100000);//数据范围
while(n--)
{
scanf("%s",opt);
if(!strcmp(opt,"Add"))
{
scanf("%d",&x);
generate(x);
}
else if(!strcmp(opt,"Remove"))//else if减少多余的strcmp调用
{
scanf("%d",&x);
remove(x);
}
else if(!strcmp(opt,"Query"))
printf("%d\n",seg[1].diff == 0x3f3f3f3f ? -1 : seg[1].diff);//注意判断-1情况
}
}
嗯,如果不理解的话,可以往上再翻一遍代码理解一下,如果还是不懂,可以离开了(逃