线段树,单点,区间修改查阅
#PermanentNotes/algorithm
思想
首先关于树有许多类型,这里我们主要首线段树,整体思想就是将一个大区间进行拆分,拆分成各个小区间,在我们进行查找,更新时,就是对区间的查找更新
类型
初始化,构建树
代码
const int Z = 1e7;
const ll INF = 1e18;
const int maxn= 1e5 + 10;
const int maxnn = maxn << 2;
int N, M, E;
ll tree[maxnn];
void build(int l, int r, int k) {
if (l == r) {
tree[k] = INF;//在这里,我经常将它的节点写成它的l
return;
}
int mid = l + r >> 1;
int lc = k << 1;
int rc = k << 1 | 1;
build(l, mid, lc);
build(mid + 1, r, rc);
tree[k] = min(tree[lc], tree[rc]);
}
在我们的初始化当中,一定要根据题意,确定我们最开始要初始的值(我们假设我们求的是一个区间最小值,那么将每个区间都初始化为最大值)
单点更新
代码
这里,我们假设求最小值
void pushup(int rt)
{
tree[rt] = min(tree[rt << 1], tree[rt << 1 | 1]);
}
void update(int pos,ll c,int l,int r,int rt)
{
if(l==r){
tree[rt]=min(tree[rt],c);
return ;
}
int m=(l+r)>>1;
if(pos<=m) update(pos,c,lson);
if(pos> m) update(pos,c,rson);
pushup(rt);
}
区间查找
我们假设查找一个区间的最小值
代码
ll query(int L, int R, int l, int r, int rt)
{
if (L <= l && r <= R) {
return tree[rt];
}
int m = (l + r) >> 1;
ll ret = inf;
int lc=rt<<1;
int rc=rt<<1|1;
if (L <= m) ret = min(ret, query(L, R, l,m,lc));
if (R > m) ret = min(ret, query(L, R, m+1,r,rc));
return ret;
}
区间修改(懒标记法)
每次在进行左右区间(lc,rc)操作之前,先进行pushdown对,对lc,rc操作完毕之后再pushup
typedef long long ll;
#define lc p<<1
#define rc p<<1|1
const int N = 1e5;
int a[N];
struct node {
int l;
int r;
ll sum;
ll add;
}tr[4 * N];
void pushup(int p) {
tr[p].sum = tr[lc].sum + tr[rc].sum;
}
void pushdown(int p) {
if (tr[p].add) {
tr[lc].sum += (tr[lc].r - tr[lc].l + 1) * tr[p].add;
tr[rc].sum += (tr[rc].r - tr[rc].l + 1) * tr[p].add;
tr[lc].add += tr[p].add;
tr[rc].add += tr[p].add;
tr[p].add = 0;
}
}
void build(int p, int l, int r) {
tr[p] = { l,r,a[l],0 };
if (l == r)return;
int m = l + r >> 1;
build(lc, l, m);
build(rc, m + 1, r);
pushup(p);
}
void update(int p, int x, int y, int k) {
if (x <= tr[p].l && tr[p].r <= y) {
tr[p].sum += (tr[p].r - tr[p].l + 1) * k;
tr[p].add += k;
return;
}
int m = tr[p].l + tr[p].r >> 1;
pushdown(p);
if (x <= m)update(lc, x, y, k);
if (m < y)update(rc, x, y, k);
pushup(p);
}
ll query(int p, int x, int y) {
if (x <= tr[p].l && tr[p].r <= y)return tr[p].sum;
int m = tr[p].l + tr[p].r >> 1;
pushdown(p);
ll sum = 0;
if (x <= m)sum += query(lc, x, y);
if (m < y)sum += query(rc, x, y);
return sum;
}
推荐视频b站up董晓算法我的算法都是在这儿学的
线段树例题
acwing243
洛谷p3372
洛谷p3373