当OI考试想不到正解,抓耳挠腮,准备爆零或者打表的时候
你或许可以考虑用线段树来打个暴力卡过或者拿60分
当然,如果你随便打个暴力,能卡个20分就算是信仰极高 幸运的玩家了
所以废物来讲一下线段树的入门,真就是入门,没学过c++也会的那种
先从思路入手,假设我们要维护一个数组(序列),假设让你在某个区间同时加上一个数或者求和区间乘一个数的时候,大多数人选择暴力求解
那你AC 寄了,单次的查询需要O(N)的复杂度,但是线段树只有O(logN),嘎嘎快
正文
---------------------------------------
重点来了,线段树的核心思路就是把整个序列从中间拆开,一直拆,一直拆,一直拆,一直拆,拆到不能拆为止,也就是说l==r时停止
大概是这样
你已经学会了最基本的提高算法的思路,试着挑战最菜的lxl吧
然后是代码的实现,从定义树开始,建树我们考虑用结构体,我们需要记录这个区间的区间和,这个区间的左边界和右边界
以及一个add(懒标记,顾名思义,很懒但是节省时间,说不定靠信仰卡过70分)笑死我了傻逼别想了
定义树代码如下
struct T{
int l,r;
ll sum,add;
}tree[4*N];
好了,你已经会定义线段树了,去挑战一个叫_rqy的新手吧
将这个树建立起来也很简单,如果说tree[i].l==tree[i].r,那么就是说我到了一个叶子节点,可以赋值
如果不是叶子节点,则我们考虑找他的左右儿子,定义一个mid=(tree[i].l+tree[i].r)/2,也就是他的左儿子的右边界,mid+1是右儿子的左边界
最后我们只需要让tree[i].sum=tree[i*2].sum+tree[i *2+1].sum就好了(当前的区间值是左右儿子的值的和)
建树代码如下
void build(int i,int l,int r){
if(tree[i].l==tree[i].r){
tree[i].sum=in_put[l];
return ;
}
int mid=(tree[i].l+tree[i].r)/2;
build(i*2,l,mid);
build(i*2+1,mid+1,r);
tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
}
前面讲到了一个叫懒标记的东西,非常之人性,非常之懒惰 就是说,如果我要访问你的儿子们,那我就把懒标记下方(将之前需要改动的数字加上)如果没有访问到,我就把他们塞到一个add里,需要的时候调用,不需要就烂肚子里
当然,加完以后要把add清零
懒标记代码如下
void add(int i){
if(tree[i].add){
tree[i*2].sum+=tree[i].add*(tree[i*2].r-tree[i*2].l+1);
tree[i*2